From abf5e3ddc39b8f4971f61fb9f8dccdb1d4635bf5 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 10 Jul 2009 14:36:28 +0200
Subject: [PATCH] Enabled the use of Debugger-specific watch/locals models.

- Modified WatchModel to handle storage of an hierarchy
  of WatchItems.
- Factored out code for asynchronous population to
  AsyncWatchModel and added a mixin for convenient
  handling
- Added base class for synchronous models.
- Implement simple, synchronous models for CDB, greatly
  simplifying code and finally getting manual
  expansion right.
Signed-off-by: hjk <qtc-committer@nokia.com>
---
 .../debugger/abstractsyncwatchmodel.cpp       | 107 +++
 ...ramecontext.h => abstractsyncwatchmodel.h} |  54 +-
 src/plugins/debugger/asyncwatchmodel.cpp      | 232 ++++++
 src/plugins/debugger/asyncwatchmodel.h        | 102 +++
 src/plugins/debugger/cdb/cdb.pri              |   8 +-
 src/plugins/debugger/cdb/cdbdebugengine.cpp   | 178 +++--
 src/plugins/debugger/cdb/cdbdebugengine.h     |  16 +-
 src/plugins/debugger/cdb/cdbdebugengine_p.h   |  13 +-
 .../debugger/cdb/cdbstacktracecontext.cpp     |  55 +-
 .../debugger/cdb/cdbstacktracecontext.h       |  17 +-
 .../debugger/cdb/cdbsymbolgroupcontext.cpp    |   1 -
 .../debugger/cdb/cdbsymbolgroupcontext.h      |  25 -
 .../debugger/cdb/cdbsymbolgroupcontext_tpl.h  |  99 ---
 src/plugins/debugger/cdb/cdbwatchmodels.cpp   | 477 +++++++++++++
 src/plugins/debugger/cdb/cdbwatchmodels.h     |  96 +++
 src/plugins/debugger/debugger.pro             |   4 +
 src/plugins/debugger/debuggermanager.cpp      |  58 +-
 src/plugins/debugger/debuggermanager.h        |   8 +-
 src/plugins/debugger/gdb/gdbengine.cpp        |  20 +-
 src/plugins/debugger/gdb/gdbengine.h          |   6 +-
 src/plugins/debugger/idebuggerengine.h        |   7 +-
 src/plugins/debugger/script/scriptengine.cpp  |  21 +-
 src/plugins/debugger/script/scriptengine.h    |   9 +-
 src/plugins/debugger/tcf/tcfengine.cpp        |  10 +-
 src/plugins/debugger/tcf/tcfengine.h          |   9 +-
 src/plugins/debugger/watchhandler.cpp         | 661 +++++++++---------
 src/plugins/debugger/watchhandler.h           | 151 ++--
 src/plugins/debugger/watchutils.cpp           |  23 +-
 src/plugins/debugger/watchwindow.cpp          |   8 +-
 29 files changed, 1788 insertions(+), 687 deletions(-)
 create mode 100644 src/plugins/debugger/abstractsyncwatchmodel.cpp
 rename src/plugins/debugger/{cdb/cdbstackframecontext.h => abstractsyncwatchmodel.h} (52%)
 create mode 100644 src/plugins/debugger/asyncwatchmodel.cpp
 create mode 100644 src/plugins/debugger/asyncwatchmodel.h
 create mode 100644 src/plugins/debugger/cdb/cdbwatchmodels.cpp
 create mode 100644 src/plugins/debugger/cdb/cdbwatchmodels.h

diff --git a/src/plugins/debugger/abstractsyncwatchmodel.cpp b/src/plugins/debugger/abstractsyncwatchmodel.cpp
new file mode 100644
index 00000000000..9cf1d8404dd
--- /dev/null
+++ b/src/plugins/debugger/abstractsyncwatchmodel.cpp
@@ -0,0 +1,107 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "abstractsyncwatchmodel.h"
+
+#include <utils/qtcassert.h>
+#include <QtCore/QDebug>
+
+namespace Debugger {
+namespace Internal {
+
+enum  { debug = 0 };
+
+AbstractSyncWatchModel::AbstractSyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
+    WatchModel(handler, type, parent)
+{
+}
+
+void AbstractSyncWatchModel::fetchMore(const QModelIndex &parent)
+{
+    WatchItem *item = watchItem(parent);
+    if (!item || item == root())
+        return;
+    if (debug)
+        qDebug() << ">fetchMore" << item->toString();
+    // Figure out children...
+    if (item->isHasChildrenNeeded()) {
+        m_errorMessage.clear();
+        if (!complete(item, &m_errorMessage)) {
+            item->setHasChildren(false);
+            emit error(m_errorMessage);
+        }
+    }
+    if (item->isChildrenNeeded()) {
+        m_errorMessage.clear();
+        if (fetchChildren(item, &m_errorMessage)) {
+            item->setChildrenUnneeded();
+        } else {
+            item->setHasChildren(false);
+            emit error(m_errorMessage);
+        }
+    }
+
+    if (debug)
+        qDebug() << "<fetchMore" << item->iname << item->children.size();
+}
+
+bool AbstractSyncWatchModel::canFetchMore(const QModelIndex &parent) const
+{
+    WatchItem *item = watchItem(parent);
+    if (!item || item == root())
+        return false;
+    const bool rc = item->isChildrenNeeded() || item->isHasChildrenNeeded();
+    if (debug)
+        qDebug() << "canFetchMore" << rc << item->iname;
+    return rc;
+}
+
+QVariant AbstractSyncWatchModel::data(const QModelIndex &idx, int role) const
+{
+    if (WatchItem *wdata = watchItem(idx)) {
+        const int column = idx.column();
+        // Is any display data (except children) needed?
+        const bool incomplete = (wdata->state & ~WatchData::ChildrenNeeded);
+        if ((debug > 1) && incomplete && column == 0 && role == Qt::DisplayRole)
+            qDebug() << "data()" << "incomplete=" << incomplete << wdata->toString();
+        if (incomplete) {
+            AbstractSyncWatchModel *nonConstThis = const_cast<AbstractSyncWatchModel *>(this);
+            m_errorMessage.clear();
+            if (!nonConstThis->complete(wdata, &m_errorMessage)) {
+                wdata->setAllUnneeded();
+                nonConstThis->emit error(m_errorMessage);
+            }
+        }
+        return WatchModel::data(*wdata, column, role);
+    }
+    return QVariant();
+}
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.h b/src/plugins/debugger/abstractsyncwatchmodel.h
similarity index 52%
rename from src/plugins/debugger/cdb/cdbstackframecontext.h
rename to src/plugins/debugger/abstractsyncwatchmodel.h
index f9ba79c9f83..91573a0602b 100644
--- a/src/plugins/debugger/cdb/cdbstackframecontext.h
+++ b/src/plugins/debugger/abstractsyncwatchmodel.h
@@ -27,49 +27,49 @@
 **
 **************************************************************************/
 
-#ifndef CDBSTACKFRAMECONTEXT_H
-#define CDBSTACKFRAMECONTEXT_H
+#ifndef ABSTRACTSYNCWATCHMODEL_H
+#define ABSTRACTSYNCWATCHMODEL_H
 
+#include "watchhandler.h"
+#include <QtCore/QPointer>
 #include <QtCore/QList>
-#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+QT_END_NAMESPACE
 
 namespace Debugger {
 namespace Internal {
 
-class WatchData;
-class WatchHandler;
-class CdbSymbolGroupContext;
-class CdbDumperHelper;
-
-/* CdbStackFrameContext manages a symbol group context and
- * a dumper context. It dispatches calls between the local items
- * that are handled by the symbol group and those that are handled by the dumpers. */
+/* AbstractSyncWatchModel: To be used by synchonous debuggers.
+ * Implements all of WatchModel and provides new virtuals for
+ * the debugger to complete items. */
 
-class CdbStackFrameContext
+class AbstractSyncWatchModel : public WatchModel
 {
-    Q_DISABLE_COPY(CdbStackFrameContext)
+    Q_OBJECT
 public:
-    explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
-                                  CdbSymbolGroupContext *symbolContext);
-    ~CdbStackFrameContext();   
+    explicit AbstractSyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0);
+
+    virtual QVariant data(const QModelIndex &index, int role) const;
 
-    bool assignValue(const QString &iname, const QString &value,
-                     QString *newValue /* = 0 */, QString *errorMessage);
-    bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
+    virtual void fetchMore(const QModelIndex &parent);
+    virtual bool canFetchMore(const QModelIndex &parent) const;
 
-    bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
+signals:
+    // Emitted if one of fetchChildren/complete fails.
+    void error(const QString &);
 
-    bool completeData(const WatchData &incompleteLocal,
-                      WatchHandler *wh,
-                      QString *errorMessage);
+protected:
+    // Overwrite these virtuals to fetch children of an item and to complete it
+    virtual bool fetchChildren(WatchItem *wd, QString *errorMessage) = 0;
+    virtual bool complete(WatchItem *wd, QString *errorMessage) = 0;
 
 private:
-    const bool m_useDumpers;
-    const QSharedPointer<CdbDumperHelper> m_dumper;
-    CdbSymbolGroupContext *m_symbolContext;
+    mutable QString m_errorMessage;
 };
 
 } // namespace Internal
 } // namespace Debugger
 
-#endif // CDBSTACKFRAMECONTEXT_H
+#endif // ABSTRACTSYNCWATCHMODEL_H
diff --git a/src/plugins/debugger/asyncwatchmodel.cpp b/src/plugins/debugger/asyncwatchmodel.cpp
new file mode 100644
index 00000000000..5b627eb8606
--- /dev/null
+++ b/src/plugins/debugger/asyncwatchmodel.cpp
@@ -0,0 +1,232 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "asyncwatchmodel.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <utils/qtcassert.h>
+
+namespace Debugger {
+namespace Internal {
+
+int AsyncWatchModel::generationCounter = 0;
+
+static const QString strNotInScope =
+        QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
+
+////////////////////////////////////////////////////////////////////
+//
+// WatchItem
+//
+////////////////////////////////////////////////////////////////////
+
+AsyncWatchModel::AsyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
+    WatchModel(handler, type, parent)
+{
+    dummyRoot()->fetchTriggered = true;
+}
+
+void AsyncWatchModel::removeOutdated()
+{
+    WatchItem *item = dummyRoot();
+    QTC_ASSERT(item, return);
+    foreach (WatchItem *child, item->children)
+        removeOutdatedHelper(child);
+#if DEBUG_MODEL
+#if USE_MODEL_TEST
+    //(void) new ModelTest(this, this);
+#endif
+#endif
+}
+
+void AsyncWatchModel::removeOutdatedHelper(WatchItem *item)
+{
+    if (item->generation < generationCounter)
+        removeItem(item);
+    else {
+        foreach (WatchItem *child, item->children)
+            removeOutdatedHelper(child);
+        item->fetchTriggered = false;
+    }
+}
+
+bool AsyncWatchModel::canFetchMore(const QModelIndex &index) const
+{
+    if (index.isValid())
+        if (const WatchItem *item = watchItem(index))
+            return !item->fetchTriggered;
+    return false;
+}
+
+void AsyncWatchModel::fetchMore(const QModelIndex &index)
+{
+    QTC_ASSERT(index.isValid(), return);
+    WatchItem *item = watchItem(index);
+    QTC_ASSERT(item && !item->fetchTriggered, return);
+    item->fetchTriggered = true;
+    WatchData data = *item;
+    data.setChildrenNeeded();
+    emit watchDataUpdateNeeded(data);
+}
+
+static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
+{
+    QString name1 = item1->iname.section('.', -1);
+    QString name2 = item2->iname.section('.', -1);
+    if (!name1.isEmpty() && !name2.isEmpty()) {
+        if (name1.at(0).isDigit() && name2.at(0).isDigit())
+            return name1.toInt() < name2.toInt();
+    }
+    return name1 < name2;
+}
+
+static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
+{
+    QList<WatchItem *>::const_iterator it =
+        qLowerBound(list.begin(), list.end(), item, iNameSorter);
+    return it - list.begin();
+}
+
+void AsyncWatchModel::insertData(const WatchData &data)
+{
+    // qDebug() << "WMI:" << data.toString();
+    QTC_ASSERT(!data.iname.isEmpty(), return);
+    WatchItem *parent = findItemByIName(parentName(data.iname), root());
+    if (!parent) {
+        WatchData parent;
+        parent.iname = parentName(data.iname);
+        insertData(parent);
+        //MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
+        return;
+    }
+    QModelIndex index = watchIndex(parent);
+    if (WatchItem *oldItem = findItemByIName(data.iname, parent)) {
+        // overwrite old entry
+        //MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
+        bool changed = !data.value.isEmpty()
+            && data.value != oldItem->value
+            && data.value != strNotInScope;
+        oldItem->setData(data);
+        oldItem->changed = changed;
+        oldItem->generation = generationCounter;
+        QModelIndex idx = watchIndex(oldItem);
+        emit dataChanged(idx, idx.sibling(idx.row(), 2));
+    } else {
+        // add new entry
+        //MODEL_DEBUG("INSERT : " << data.iname << data.value);
+        WatchItem *item = new WatchItem(data);
+        item->parent = parent;
+        item->generation = generationCounter;
+        item->changed = true;
+        int n = findInsertPosition(parent->children, item);
+        beginInsertRows(index, n, n);
+        parent->children.insert(n, item);
+        endInsertRows();
+    }
+}
+
+// ----------------- AsyncWatchModelMixin
+AsyncWatchModelMixin::AsyncWatchModelMixin(WatchHandler *wh,
+                                           QObject *parent) :
+    QObject(parent),
+    m_wh(wh)
+{
+}
+
+AsyncWatchModelMixin::~AsyncWatchModelMixin()
+{
+}
+
+AsyncWatchModel *AsyncWatchModelMixin::model(int wt) const
+{
+    if (wt < 0 ||  wt >= WatchModelCount)
+        return 0;
+    if (!m_models[wt]) {
+        m_models[wt] = new AsyncWatchModel(m_wh, static_cast<WatchType>(wt), const_cast<AsyncWatchModelMixin *>(this));
+        connect(m_models[wt], SIGNAL(watchDataUpdateNeeded(WatchData)), this, SIGNAL(watchDataUpdateNeeded(WatchData)));
+        m_models[wt]->setObjectName(QLatin1String("model") + QString::number(wt));
+    }
+    return m_models[wt];
+}
+
+AsyncWatchModel *AsyncWatchModelMixin::modelForIName(const QString &iname) const
+{
+    const WatchType wt = WatchHandler::watchTypeOfIName(iname);
+    QTC_ASSERT(wt != WatchModelCount, return 0);
+    return model(wt);
+}
+
+void AsyncWatchModelMixin::beginCycle()
+{
+    ++AsyncWatchModel::generationCounter;
+}
+
+void AsyncWatchModelMixin::updateWatchers()
+{
+    //qDebug() << "UPDATE WATCHERS";
+    // copy over all watchers and mark all watchers as incomplete
+    foreach (const QString &exp, m_wh->watcherExpressions()) {
+        WatchData data;
+        data.iname = m_wh->watcherName(exp);
+        data.setAllNeeded();
+        data.name = exp;
+        data.exp = exp;
+        insertData(data);
+    }
+}
+
+void AsyncWatchModelMixin::endCycle()
+{
+    for (int m = 0; m < WatchModelCount; m++)
+        if (m_models[m])
+            m_models[m]->removeOutdated();
+}
+
+void AsyncWatchModelMixin::insertWatcher(const WatchData &data)
+{
+    // Is the engine active?
+    if (m_models[WatchersWatch] && m_wh->model(WatchersWatch) == m_models[WatchersWatch])
+        insertData(data);
+}
+
+void AsyncWatchModelMixin::insertData(const WatchData &data)
+{
+    QTC_ASSERT(data.isValid(), return);
+    if (data.isSomethingNeeded()) {
+        emit watchDataUpdateNeeded(data);
+    } else {
+        AsyncWatchModel *model = modelForIName(data.iname);
+        QTC_ASSERT(model, return);
+        model->insertData(data);
+    }
+}
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/asyncwatchmodel.h b/src/plugins/debugger/asyncwatchmodel.h
new file mode 100644
index 00000000000..880f2f83d76
--- /dev/null
+++ b/src/plugins/debugger/asyncwatchmodel.h
@@ -0,0 +1,102 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef ASYNCWATCHMODEL_H
+#define ASYNCWATCHMODEL_H
+
+#include "watchhandler.h"
+#include <QtCore/QPointer>
+
+namespace Debugger {
+namespace Internal {
+
+/* AsyncWatchModel: incrementally populated by asynchronous debuggers via
+ * fetch. */
+
+class AsyncWatchModel : public WatchModel
+{
+    Q_OBJECT
+public:
+    explicit AsyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0);
+
+    bool canFetchMore(const QModelIndex &parent) const;
+    void fetchMore(const QModelIndex &parent);
+
+    friend class WatchHandler;
+    friend class GdbEngine;
+
+    void insertData(const WatchData &data);
+
+    void removeOutdated();
+    void removeOutdatedHelper(WatchItem *item);
+
+    void setActiveData(const QString &data) { m_activeData = data; }
+
+    static int generationCounter;
+
+signals:
+    void watchDataUpdateNeeded(const WatchData &data);
+
+private:
+    QString m_activeData;
+    WatchItem *m_root;
+};
+
+/* A Mixin to manage asynchronous models. */
+
+class AsyncWatchModelMixin :  public QObject {
+    Q_OBJECT
+public:
+    explicit AsyncWatchModelMixin(WatchHandler *wh, QObject *parent = 0);
+    virtual ~AsyncWatchModelMixin();
+
+    AsyncWatchModel *model(int t) const;
+    AsyncWatchModel *modelForIName(const QString &iname) const;
+
+    void beginCycle(); // called at begin of updateLocals() cycle
+    void updateWatchers(); // called after locals are fetched
+    void endCycle(); // called after all results have been received
+
+public slots:
+    void insertData(const WatchData &data);
+    // For watchers, ensures that engine is active
+    void insertWatcher(const WatchData &data);
+
+signals:
+    void watchDataUpdateNeeded(const WatchData &data);
+
+private:
+    WatchHandler *m_wh;
+    mutable QPointer<AsyncWatchModel> m_models[WatchModelCount];
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // ASYNCWATCHMODEL_H
diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri
index 06f14409d32..3c7398567b8 100644
--- a/src/plugins/debugger/cdb/cdb.pri
+++ b/src/plugins/debugger/cdb/cdb.pri
@@ -39,7 +39,6 @@ HEADERS += \
     $$PWD/cdbsymbolgroupcontext.h \
     $$PWD/cdbsymbolgroupcontext_tpl.h \
     $$PWD/cdbstacktracecontext.h \
-    $$PWD/cdbstackframecontext.h \
     $$PWD/cdbbreakpoint.h \
     $$PWD/cdbmodules.h \
     $$PWD/cdbassembler.h \
@@ -47,14 +46,14 @@ HEADERS += \
     $$PWD/cdboptionspage.h \
     $$PWD/cdbdumperhelper.h \
     $$PWD/cdbsymbolpathlisteditor.h \
-    $$PWD/cdbexceptionutils.h
+    $$PWD/cdbexceptionutils.h \
+    $$PWD/cdbwatchmodels.h
 
 SOURCES += \
     $$PWD/cdbdebugengine.cpp \
     $$PWD/cdbdebugeventcallback.cpp \
     $$PWD/cdbdebugoutput.cpp \
     $$PWD/cdbsymbolgroupcontext.cpp \
-    $$PWD/cdbstackframecontext.cpp \
     $$PWD/cdbstacktracecontext.cpp \
     $$PWD/cdbbreakpoint.cpp \
     $$PWD/cdbmodules.cpp \
@@ -63,7 +62,8 @@ SOURCES += \
     $$PWD/cdboptionspage.cpp \
     $$PWD/cdbdumperhelper.cpp \
     $$PWD/cdbsymbolpathlisteditor.cpp \
-    $$PWD/cdbexceptionutils.cpp
+    $$PWD/cdbexceptionutils.cpp \
+    $$PWD/cdbwatchmodels.cpp
 
 FORMS += $$PWD/cdboptionspagewidget.ui
 
diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp
index 798f5234ff4..867cfb5a91c 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine.cpp
+++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp
@@ -30,7 +30,6 @@
 #include "cdbdebugengine.h"
 #include "cdbdebugengine_p.h"
 #include "cdbstacktracecontext.h"
-#include "cdbstackframecontext.h"
 #include "cdbsymbolgroupcontext.h"
 #include "cdbbreakpoint.h"
 #include "cdbmodules.h"
@@ -47,6 +46,7 @@
 #include "moduleshandler.h"
 #include "disassemblerhandler.h"
 #include "watchutils.h"
+#include "cdbwatchmodels.h"
 
 #include <coreplugin/icore.h>
 #include <utils/qtcassert.h>
@@ -301,7 +301,9 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
     m_debuggerManagerAccess(parent->engineInterface()),
     m_currentStackTrace(0),
     m_firstActivatedFrame(true),
-    m_mode(AttachCore)
+    m_mode(AttachCore),
+    m_localsModel(0),
+    m_watchModel(0)
 {
 }
 
@@ -390,6 +392,15 @@ CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
         m_cif.debugDataSpaces->Release();
 }
 
+void CdbDebugEnginePrivate::saveLocalsViewState()
+{
+    if (m_localsModel && m_localsModel->symbolGroupContext()) {
+        const int oldFrame = m_debuggerManagerAccess->stackHandler()->currentIndex();
+        if (oldFrame != -1)
+            m_debuggerManagerAccess->watchHandler()->saveLocalsViewState(oldFrame);
+    }
+}
+
 void CdbDebugEnginePrivate::clearForRun()
 {
     if (debugCDB)
@@ -403,6 +414,10 @@ void CdbDebugEnginePrivate::clearForRun()
 
 void CdbDebugEnginePrivate::cleanStackTrace()
 {
+    if (m_localsModel) {
+        saveLocalsViewState();
+        m_localsModel->setSymbolGroupContext(0);
+    }
     if (m_currentStackTrace) {
         delete m_currentStackTrace;
         m_currentStackTrace = 0;
@@ -465,14 +480,19 @@ QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &functio
     QString errorMessage;
     QString rc;
     // Find the frame of the function if there is any
-    CdbStackFrameContext *frame = 0;
+    const QString iname = QLatin1String("local.") + exp;
     if (m_d->m_currentStackTrace &&  !function.isEmpty()) {
         const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
-        if (frameIndex != -1)
-            frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
+        if (frameIndex != -1) {
+            // Are we at the current frame?
+            if (frameIndex == m_d->m_debuggerManagerAccess->stackHandler()->currentIndex()) {
+                if (const WatchData *wd = m_d->m_localsModel->findItemByIName(iname, m_d->m_localsModel->root()))
+                    return wd->toToolTip();                            }
+            // Nope, try to retrieve via another symbol group
+            if (m_d->m_currentStackTrace->editorToolTip(frameIndex, iname, &rc, &errorMessage))
+                return rc;
+        }
     }
-    if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
-        return rc;
     // No function/symbol context found, try to evaluate in current context.
     // Do not append type as this will mostly be 'long long' for integers, etc.
     QString type;
@@ -770,67 +790,6 @@ void CdbDebugEngine::detachDebugger()
     m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
 }
 
-CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
-{
-    if (!m_currentStackTrace) {
-        *errorMessage = QLatin1String(msgNoStackTraceC);
-        return 0;
-    }
-    if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
-        return sg;
-    return 0;
-}
-
-void CdbDebugEngine::evaluateWatcher(WatchData *wd)
-{
-    if (debugCDBWatchHandling)
-        qDebug() << Q_FUNC_INFO << wd->exp;
-    QString errorMessage;
-    QString value;
-    QString type;
-    if (evaluateExpression(wd->exp, &value, &type, &errorMessage)) {
-        wd->setValue(value);
-        wd->setType(type);
-    } else {
-        wd->setValue(errorMessage);
-        wd->setTypeUnneeded();
-    }
-    wd->setHasChildren(false);
-}
-
-void CdbDebugEngine::updateWatchData(const WatchData &incomplete)
-{
-    // Watch item was edited while running
-    if (m_d->isDebuggeeRunning())
-        return;
-
-    if (debugCDBWatchHandling)
-        qDebug() << Q_FUNC_INFO << "\n    " << incomplete.toString();
-
-    WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
-    if (incomplete.iname.startsWith(QLatin1String("watch."))) {
-        WatchData watchData = incomplete;
-        evaluateWatcher(&watchData);
-        watchHandler->insertData(watchData);
-        return;
-    }
-
-    const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex();
-
-    bool success = false;
-    QString errorMessage;
-    do {
-        CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
-        if (!sg)
-            break;
-        if (!sg->completeData(incomplete, watchHandler, &errorMessage))
-            break;
-        success = true;
-    } while (false);
-    if (!success)
-        warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
-}
-
 void CdbDebugEngine::stepExec()
 {
     if (debugCDB)
@@ -1037,18 +996,14 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
     bool success = false;
     do {
         QString newValue;
-        CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
-        if (!sg)
-            break;
-        if (!sg->assignValue(expr, value, &newValue, &errorMessage))
+        if (frameIndex < 0 || !m_d->m_currentStackTrace) {
+            errorMessage = tr("No current stack trace.");
             break;
-        // Update view
-        WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
-        if (WatchData *fwd = watchHandler->findItem(expr)) {
-            fwd->setValue(newValue);
-            watchHandler->insertData(*fwd);
-            watchHandler->updateWatchers();
         }
+        // Assign in stack and update view
+        if (!m_d->m_currentStackTrace->assignValue(frameIndex, expr, value, &newValue, &errorMessage))
+            break;
+        m_d->m_localsModel->setValueByExpression(expr, newValue);
         success = true;
     } while (false);
     if (!success) {
@@ -1078,6 +1033,11 @@ bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const Q
     return true;
 }
 
+bool CdbDebugEngine::isDebuggeeHalted() const
+{
+    return m_d->m_hDebuggeeProcess && !m_d->isDebuggeeRunning();
+}
+
 bool CdbDebugEngine::evaluateExpression(const QString &expression,
                                         QString *value,
                                         QString *type,
@@ -1132,21 +1092,21 @@ void CdbDebugEngine::activateFrame(int frameIndex)
     bool success = false;
     do {
         StackHandler *stackHandler = m_d->m_debuggerManagerAccess->stackHandler();
-        WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
         const int oldIndex = stackHandler->currentIndex();
         if (frameIndex >= stackHandler->stackSize()) {
             errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler->stackSize());
             break;
         }
 
-        if (oldIndex != frameIndex)
+        if (oldIndex != frameIndex) {
+            m_d->saveLocalsViewState();
             stackHandler->setCurrentIndex(frameIndex);
+        }
 
         const StackFrame &frame = stackHandler->currentFrame();
         if (!frame.isUsable()) {
+            m_d->m_localsModel->setSymbolGroupContext(0);
             // Clean out model
-            watchHandler->beginCycle();
-            watchHandler->endCycle();
             errorMessage = QString::fromLatin1("%1: file %2 unusable.").
                            arg(QLatin1String(Q_FUNC_INFO), frame.file);
             break;
@@ -1155,10 +1115,18 @@ void CdbDebugEngine::activateFrame(int frameIndex)
         m_d->m_debuggerManager->gotoLocation(frame.file, frame.line, true);
 
         if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
-            watchHandler->beginCycle();
-            if (CdbStackFrameContext *sgc = m_d->getStackFrameContext(frameIndex, &errorMessage))
-                success = sgc->populateModelInitially(watchHandler, &errorMessage);
-            watchHandler->endCycle();
+            m_d->m_localsModel->setUseDumpers(m_d->m_dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers));
+            CdbSymbolGroupContext *ctx = 0;
+            if (m_d->m_currentStackTrace) {
+                ctx = m_d->m_currentStackTrace->symbolGroupAt(frameIndex, &errorMessage);
+                success = ctx != 0;
+            } else {
+                errorMessage = QLatin1String(msgNoStackTraceC);
+            }
+            m_d->m_localsModel->setSymbolGroupContext(ctx);
+            m_d->m_debuggerManagerAccess->watchHandler()->restoreLocalsViewState(frameIndex);
+        } else {
+            success = true;
         }
     } while (false);
     if (!success)
@@ -1222,7 +1190,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
                                                  m_cif.debugSymbols,
                                                  m_debuggerManagerAccess->breakHandler(),
                                                  errorMessage, &warnings);
-    if (const int warningsCount = warnings.size())        
+    if (const int warningsCount = warnings.size())
         for (int w = 0; w < warningsCount; w++)
             m_engine->warning(warnings.at(w));
     return ok;
@@ -1500,7 +1468,8 @@ void CdbDebugEnginePrivate::updateStackTrace()
         m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current);
         m_engine->activateFrame(current);
     }
-    m_debuggerManagerAccess->watchHandler()->updateWatchers();
+    // Update watchers
+    m_watchModel->refresh();
 }
 
 void CdbDebugEnginePrivate::updateModules()
@@ -1512,8 +1481,6 @@ void CdbDebugEnginePrivate::updateModules()
     m_debuggerManagerAccess->modulesHandler()->setModules(modules);
 }
 
-static const char *dumperPrefixC = "dumper";
-
 void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
 {
     if (debugCDB>2)
@@ -1586,6 +1553,37 @@ bool CdbDebugEnginePrivate::setSymbolPaths(const QStringList &s, QString *errorM
     return true;
 }
 
+void CdbDebugEngine::insertWatcher(const WatchData &wd)
+{
+    // Make sure engine is active
+    if (m_d->m_watchModel && m_d->m_watchModel == m_d->m_debuggerManagerAccess->watchHandler()->model(WatchersWatch))
+        m_d->m_watchModel->addWatcher(wd);
+}
+
+WatchModel *CdbDebugEngine::watchModel(int type) const
+{
+    switch (type) {
+    case LocalsWatch:
+        if (!m_d->m_localsModel) {
+            m_d->m_localsModel = new CdbLocalsModel(m_d->m_dumper, m_d->m_debuggerManagerAccess->watchHandler(), LocalsWatch, const_cast<CdbDebugEngine*>(this));
+            connect(m_d->m_localsModel, SIGNAL(error(QString)), this, SLOT(warning(QString)));
+        }
+        return m_d->m_localsModel;
+    case WatchersWatch:
+        if (!m_d->m_watchModel) {
+            CdbDebugEngine* nonConstThis = const_cast<CdbDebugEngine*>(this);
+            WatchHandler *wh = m_d->m_debuggerManagerAccess->watchHandler();
+            m_d->m_watchModel = new CdbWatchModel(nonConstThis, m_d->m_dumper, wh, WatchersWatch, nonConstThis);
+            connect(m_d->m_watchModel, SIGNAL(error(QString)), this, SLOT(warning(QString)));
+            connect(wh, SIGNAL(watcherInserted(WatchData)), this, SLOT(insertWatcher(WatchData)));
+        }
+        return m_d->m_watchModel;
+    default:
+        break;
+    }
+    return 0;
+}
+
 } // namespace Internal
 } // namespace Debugger
 
diff --git a/src/plugins/debugger/cdb/cdbdebugengine.h b/src/plugins/debugger/cdb/cdbdebugengine.h
index 13725bb97e4..8c827769709 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine.h
+++ b/src/plugins/debugger/cdb/cdbdebugengine.h
@@ -64,15 +64,14 @@ public:
     virtual bool startDebugger(const QSharedPointer<DebuggerStartParameters> &startParameters);
     virtual void exitDebugger();
     virtual void detachDebugger();
-    virtual void updateWatchData(const WatchData &data);
 
     virtual void stepExec();
     virtual void stepOutExec();
     virtual void nextExec();
     virtual void stepIExec();
     virtual void nextIExec();
-    
-    virtual void continueInferior();    
+
+    virtual void continueInferior();
     virtual void interruptInferior();
 
     virtual void runToLineExec(const QString &fileName, int lineNumber);
@@ -97,6 +96,11 @@ public:
     virtual void reloadSourceFiles();
     virtual void reloadFullStack() {}
 
+    WatchModel *watchModel(int type) const;
+
+    bool isDebuggeeHalted() const;
+    bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
+
 public slots:
     void syncDebuggerPaths();
 
@@ -106,8 +110,9 @@ protected:
 private slots:
     void slotConsoleStubStarted();
     void slotConsoleStubError(const QString &msg);
-    void slotConsoleStubTerminated();    
+    void slotConsoleStubTerminated();
     void warning(const QString &w);
+    void insertWatcher(const WatchData &);
 
 private:
     bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
@@ -116,8 +121,7 @@ private:
     void killWatchTimer();
     void processTerminated(unsigned long exitCode);
     bool executeDebuggerCommand(const QString &command, QString *errorMessage);
-    bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
-    void evaluateWatcher(WatchData *wd);
+
     QString editorToolTip(const QString &exp, const QString &function);
 
     CdbDebugEnginePrivate *m_d;
diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h
index 23eb5648275..910486aa8b5 100644
--- a/src/plugins/debugger/cdb/cdbdebugengine_p.h
+++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h
@@ -39,6 +39,7 @@
 
 #include <utils/consoleprocess.h>
 #include <QtCore/QSharedPointer>
+#include <QtCore/QPointer>
 #include <QtCore/QMap>
 
 namespace Debugger {
@@ -49,6 +50,8 @@ class IDebuggerManagerAccessForEngines;
 class WatchHandler;
 class CdbStackFrameContext;
 class CdbStackTraceContext;
+class CdbLocalsModel;
+class CdbWatchModel;
 
 // Thin wrapper around the 'DBEng' debugger engine shared library
 // which is loaded at runtime.
@@ -115,7 +118,7 @@ struct CdbDebugEnginePrivate
 
     bool isDebuggeeRunning() const { return m_watchTimer != -1; }
     void handleDebugEvent();
-    void updateThreadList();    
+    void updateThreadList();
     void updateStackTrace();
     void updateModules();
 
@@ -123,7 +126,6 @@ struct CdbDebugEnginePrivate
     void cleanStackTrace();
     void clearForRun();
     void handleModuleLoad(const QString &);
-    CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
     void clearDisplay();
 
     bool interruptInterferiorProcess(QString *errorMessage);
@@ -137,6 +139,8 @@ struct CdbDebugEnginePrivate
     enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto };
     void endDebugging(EndDebuggingMode em = EndDebuggingAuto);
 
+    void saveLocalsViewState();
+
     static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
     static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
 
@@ -155,7 +159,7 @@ struct CdbDebugEnginePrivate
     int                     m_watchTimer;
     CdbComInterfaces        m_cif;
     CdbDebugEventCallback   m_debugEventCallBack;
-    CdbDebugOutput          m_debugOutputCallBack;    
+    CdbDebugOutput          m_debugOutputCallBack;
     QSharedPointer<CdbDumperHelper> m_dumper;
 
     CdbDebugEngine* m_engine;
@@ -168,6 +172,9 @@ struct CdbDebugEnginePrivate
 
     DebuggerStartMode m_mode;
     Core::Utils::ConsoleProcess m_consoleStubProc;
+
+    QPointer<CdbLocalsModel> m_localsModel;
+    QPointer<CdbWatchModel> m_watchModel;
 };
 
 // helper functions
diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp
index 7663f315d0a..d3d927cd49f 100644
--- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp
+++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp
@@ -28,11 +28,11 @@
 **************************************************************************/
 
 #include "cdbstacktracecontext.h"
-#include "cdbstackframecontext.h"
 #include "cdbbreakpoint.h"
 #include "cdbsymbolgroupcontext.h"
 #include "cdbdebugengine_p.h"
 #include "cdbdumperhelper.h"
+#include "debuggeractions.h"
 
 #include <QtCore/QDir>
 #include <QtCore/QTextStream>
@@ -90,7 +90,7 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
         qDebug() << Q_FUNC_INFO << frameCount;
 
     m_frameContexts.resize(frameCount);
-    qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
+    qFill(m_frameContexts, static_cast<CdbSymbolGroupContext*>(0));
 
     // Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
     WCHAR wszBuf[MAX_PATH];
@@ -145,7 +145,7 @@ static inline QString msgFrameContextFailed(int index, const StackFrame &f, cons
             arg(index).arg(f.function).arg(f.line).arg(f.file, why);
 }
 
-CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
+CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupAt(int index, QString *errorMessage)
 {
     // Create a frame on demand
     if (debugCDB)
@@ -158,6 +158,7 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e
     }
     if (m_frameContexts.at(index))
         return m_frameContexts.at(index);
+    // Create COM and wrap
     CIDebugSymbolGroup *sg  = createSymbolGroup(index, errorMessage);
     if (!sg) {
         *errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
@@ -168,9 +169,8 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e
         *errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
         return 0;
     }
-    CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
-    m_frameContexts[index] = fr;
-    return fr;
+    m_frameContexts[index] = sc;
+    return sc;
 }
 
 CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
@@ -198,6 +198,49 @@ CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *
     return sg;
 }
 
+bool CdbStackTraceContext::editorToolTip(int frameIndex,
+                                         const QString &iname,
+                                         QString *value,
+                                         QString *errorMessage)
+{
+    value->clear();
+    // Look up iname in the frame's symbol group.
+    CdbSymbolGroupContext *m_symbolContext = symbolGroupAt(frameIndex, errorMessage);
+    if (!m_symbolContext)
+        return false;
+    unsigned long index;
+    if (!m_symbolContext->lookupPrefix(iname, &index)) {
+        *errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
+        return false;
+    }
+    const WatchData wd = m_symbolContext->symbolAt(index);
+    // Check dumpers. Should actually be just one item.
+    if (theDebuggerBoolSetting(UseDebuggingHelpers) && m_dumper->isEnabled()) {
+        QList<WatchData> result;
+        if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, 1, &result, errorMessage))  {
+            foreach (const WatchData &dwd, result) {
+                if (!value->isEmpty())
+                    value->append(QLatin1Char('\n'));
+                value->append(dwd.toToolTip());
+            }
+            return true;
+        } // Dumped ok
+    }     // has Dumpers
+    *value = wd.toToolTip();
+    return true;
+}
+
+bool CdbStackTraceContext::assignValue(int frameIndex,
+                                       const QString &iname,
+                                       const QString &value,
+                                       QString *newValue /* = 0 */, QString *errorMessage)
+{
+    if (CdbSymbolGroupContext *symbolContext = symbolGroupAt(frameIndex, errorMessage))
+        return symbolContext->assignValue(iname, value, newValue, errorMessage);
+    return  false;
+}
+
+
 QString CdbStackTraceContext::toString() const
 {
     QString rc;
diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.h b/src/plugins/debugger/cdb/cdbstacktracecontext.h
index 9dfbdf04745..d878472450b 100644
--- a/src/plugins/debugger/cdb/cdbstacktracecontext.h
+++ b/src/plugins/debugger/cdb/cdbstacktracecontext.h
@@ -47,11 +47,10 @@ namespace Internal {
 
 struct CdbComInterfaces;
 class CdbSymbolGroupContext;
-class CdbStackFrameContext;
 class CdbDumperHelper;
 
 /* Context representing a break point stack consisting of several frames.
- * Maintains an on-demand constructed list of CdbStackFrameContext
+ * Maintains an on-demand constructed list of CdbSymbolGroupContext
  * containining the local variables of the stack. */
 
 class CdbStackTraceContext        
@@ -75,7 +74,17 @@ public:
     // Top-Level instruction offset for disassembler
     ULONG64 instructionOffset() const { return m_instructionOffset; }
 
-    CdbStackFrameContext *frameContextAt(int index, QString *errorMessage);
+    CdbSymbolGroupContext *symbolGroupAt(int index, QString *errorMessage);
+
+    // Helper to retrieve an editor tooltip for a frame. Note that
+    // for the current frame, the LocalsModel should be consulted first.
+    bool editorToolTip(int frameIndex,const QString &iname,  QString *value, QString *errorMessage);
+
+    // Assign value in Debugger
+    bool assignValue(int frameIndex,
+                     const QString &iname,
+                     const QString &value,
+                     QString *newValue /* = 0 */, QString *errorMessage);
 
     // Format for logging
     void format(QTextStream &str) const;
@@ -89,7 +98,7 @@ private:
     CdbComInterfaces *m_cif;
 
     DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
-    QVector <CdbStackFrameContext*> m_frameContexts;
+    QVector <CdbSymbolGroupContext*> m_frameContexts;
     QList<StackFrame> m_frames;
     ULONG64 m_instructionOffset;
 };
diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
index 5d82835f53d..94bf9814f62 100644
--- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
@@ -406,7 +406,6 @@ WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const
     const QString value = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
     wd.setType(type);
     wd.setValue(fixValue(value));
-    wd.setChildrenNeeded(); // compensate side effects of above setters
     // Figure out children. The SubElement is only a guess unless the symbol,
     // is expanded, so, we leave this as a guess for later updates.
     // If the symbol has children (expanded or not), we leave the 'Children' flag
diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h
index 91ec5fd9240..16b7f5fab61 100644
--- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h
@@ -81,31 +81,6 @@ public:
     bool assignValue(const QString &iname, const QString &value,
                      QString *newValue /* = 0 */, QString *errorMessage);
 
-    // Initially populate the locals model for a new stackframe.
-    // Write a sequence of WatchData to it, recurse down if the
-    // recursionPredicate agrees. The ignorePredicate can be used
-    // to terminate processing after insertion of an item (if the calling
-    // routine wants to insert another subtree).
-    template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
-    static bool populateModelInitially(CdbSymbolGroupContext *sg,
-                                       OutputIterator it,
-                                       RecursionPredicate recursionPredicate,
-                                       IgnorePredicate ignorePredicate,
-                                       QString *errorMessage);
-
-    // Complete children of a WatchData item.
-    // Write a sequence of WatchData to it, recurse if the
-    // recursionPredicate agrees. The ignorePredicate can be used
-    // to terminate processing after insertion of an item (if the calling
-    // routine wants to insert another subtree).
-    template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
-    static bool completeData (CdbSymbolGroupContext *sg,
-                              WatchData incompleteLocal,
-                              OutputIterator it,
-                              RecursionPredicate recursionPredicate,
-                              IgnorePredicate ignorePredicate,
-                              QString *errorMessage);
-
     // Retrieve child symbols of prefix as a sequence of WatchData.
     template <class OutputIterator>
             bool getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage);
diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h
index ccaba97ee89..5aa9c35a2fd 100644
--- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h
@@ -66,105 +66,6 @@ bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterato
     return true;
 }
 
-// Insert a symbol (and its first level children depending on forceRecursion)
-// The parent symbol is inserted before the children to make dumper handling
-// simpler. In theory, it can happen that the symbol context indicates
-// children but can expand none, which would lead to invalid parent information
-// (expand icon), though (ignore for simplicity).
-template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
-bool insertSymbolRecursion(WatchData wd,
-                           CdbSymbolGroupContext *sg,
-                           OutputIterator it,
-                           RecursionPredicate recursionPredicate,
-                           IgnorePredicate ignorePredicate,
-                           QString *errorMessage)
-{
-    // Find out whether to recurse (has children and predicate agrees)
-    const bool hasChildren = wd.hasChildren || wd.isChildrenNeeded();
-    const bool recurse = hasChildren && recursionPredicate(wd);
-    if (debugSgRecursion)
-        qDebug() << "insertSymbolRecursion" << '\n' << wd.iname << "recurse=" << recurse;
-    if (!recurse) {
-        // No further recursion at this level, pretend entry is complete
-        // to the watchmodel for the parent to show (only remaining watchhandler-specific
-        // part here).
-        if (wd.isChildrenNeeded()) {
-            wd.setHasChildren(true);
-            wd.setChildrenUnneeded();
-        }
-        if (debugSgRecursion)
-            qDebug() << " INSERTING non-recursive: " << wd.toString();
-        *it = wd;
-        ++it;
-        return true;
-    }
-    // Recursion: Indicate children unneeded
-    wd.setHasChildren(true);
-    wd.setChildrenUnneeded();
-    if (debugSgRecursion)
-        qDebug() << " INSERTING recursive: " << wd.toString();
-    *it = wd;
-    ++it;
-    // Recurse unless the predicate disagrees
-    if (ignorePredicate(wd))
-        return true;
-    QList<WatchData> watchList;
-    // This implicitly enforces expansion
-    if (!sg->getChildSymbols(wd.iname, WatchDataBackInserter(watchList), errorMessage))
-        return false;
-    const int childCount = watchList.size();
-    for (int c = 0; c < childCount; c++) {
-        const WatchData &cwd = watchList.at(c);
-        if (wd.isValid()) { // We sometimes get empty names for deeply nested data
-            if (!insertSymbolRecursion(cwd, sg, it, recursionPredicate, ignorePredicate, errorMessage))
-                return false;
-        }  else {
-            const QString msg = QString::fromLatin1("WARNING: Skipping invalid child symbol #%2 (type %3) of '%4'.").
-                                arg(QLatin1String(Q_FUNC_INFO)).arg(c).arg(cwd.type, wd.iname);
-            qWarning("%s\n", qPrintable(msg));
-        }
-    }
-    return true;
-}
-
-template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
-bool CdbSymbolGroupContext::populateModelInitially(CdbSymbolGroupContext *sg,
-                                                   OutputIterator it,
-                                                   RecursionPredicate recursionPredicate,
-                                                   IgnorePredicate ignorePredicate,
-                                                   QString *errorMessage)
-{
-    if (debugSgRecursion)
-        qDebug() << "### CdbSymbolGroupContext::populateModelInitially";
-
-    // Insert root items
-    QList<WatchData> watchList;
-    if (!sg->getChildSymbols(sg->prefix(), WatchDataBackInserter(watchList), errorMessage))
-        return false;
-    // Insert data
-    foreach(const WatchData &wd, watchList)
-        if (!insertSymbolRecursion(wd, sg, it, recursionPredicate, ignorePredicate, errorMessage))
-            return false;
-    return true;
-}
-
-template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
-bool CdbSymbolGroupContext::completeData(CdbSymbolGroupContext *sg,
-                                         WatchData incompleteLocal,
-                                         OutputIterator it,
-                                         RecursionPredicate recursionPredicate,
-                                         IgnorePredicate ignorePredicate,
-                                         QString *errorMessage)
-{
-    if (debugSgRecursion)
-        qDebug().nospace() << "###>CdbSymbolGroupContext::completeData" << ' ' << incompleteLocal.iname << '\n';
-    // If the symbols are already expanded in the context, they will be re-inserted,
-    // which is not handled for simplicity.
-    if (!insertSymbolRecursion(incompleteLocal, sg, it, recursionPredicate, ignorePredicate, errorMessage))
-        return false;
-    return true;
-}
-
 } // namespace Internal
 } // namespace Debugger
 
diff --git a/src/plugins/debugger/cdb/cdbwatchmodels.cpp b/src/plugins/debugger/cdb/cdbwatchmodels.cpp
new file mode 100644
index 00000000000..0f5bb70846f
--- /dev/null
+++ b/src/plugins/debugger/cdb/cdbwatchmodels.cpp
@@ -0,0 +1,477 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#include "cdbwatchmodels.h"
+#include "cdbdumperhelper.h"
+#include "cdbsymbolgroupcontext.h"
+#include "cdbdebugengine.h"
+#include "watchutils.h"
+#include "debuggeractions.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QList>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QRegExp>
+
+enum { debugCDBWatchHandling = 0 };
+
+namespace Debugger {
+namespace Internal {
+
+enum { LocalsOwnerSymbolGroup, LocalsOwnerDumper };
+
+static inline QString msgNoStackFrame()
+{
+    return QCoreApplication::translate("CdbWatchModels", "No active stack frame present.");
+}
+
+static inline QString msgUnknown()
+{
+    return QCoreApplication::translate("CdbWatchModels", "<Unknown>");
+}
+
+// Helper to add a sequence of WatchData from the symbol group context to an item
+// without using dumpers.
+
+class SymbolGroupInserter
+{
+public:
+    explicit SymbolGroupInserter(WatchItem *parent) : m_parent(parent) {}
+
+    inline SymbolGroupInserter& operator*() { return *this; }
+    inline SymbolGroupInserter&operator=(const WatchData &wd) {
+        WatchItem *item = new WatchItem(wd);
+        item->source = LocalsOwnerSymbolGroup;
+        m_parent->addChild(item);
+        return *this;
+    }
+    inline SymbolGroupInserter&operator++() { return *this; }
+
+private:
+    WatchItem *m_parent;
+};
+
+// fixDumperResult: When querying an item, the queried item is sometimes returned in
+// incomplete form. Take over values from source, set items with missing addresses to
+// "complete".
+static inline void fixDumperResult(const WatchData &source,
+                                   QList<WatchData> *result)
+{
+    if (debugCDBWatchHandling > 1) {
+        qDebug() << "fixDumperResult for : " << source.toString();
+        foreach (const WatchData &wd, *result)
+            qDebug() << "   " << wd.toString();
+    }
+    const int size = result->size();
+    if (!size)
+        return;
+    WatchData &returned = result->front();
+    if (returned.iname != source.iname)
+        return;
+    if (returned.type.isEmpty())
+        returned.setType(source.type);
+    if (returned.isValueNeeded()) {
+        if (source.isValueKnown()) {
+            returned.setValue(source.value);
+        } else {
+            // Should not happen
+            returned.setValue(msgUnknown());
+            qWarning("%s: No value for %s\n", Q_FUNC_INFO, qPrintable(returned.toString()));
+        }
+    }
+    if (size == 1)
+        return;
+    // Fix the children: If the address is missing, we cannot query any further.
+    const QList<WatchData>::iterator wend = result->end();
+    QList<WatchData>::iterator it = result->begin();
+    for (++it; it != wend; ++it) {
+        WatchData &wd = *it;
+        if (wd.addr.isEmpty() && wd.isSomethingNeeded()) {
+            wd.setAllUnneeded();
+        }
+    }
+}
+
+// Dump an item. If *ptrToDumpedItem == 0, allocate a new item and set it.
+// If it is non-null, the item pointed to will receive the results
+// ("complete" functionality).
+static CdbDumperHelper::DumpResult
+        dumpItem(const QSharedPointer<CdbDumperHelper> dumper,
+                 const WatchData &wd,
+                 WatchItem **ptrToDumpedItem,
+                 int dumperOwnerValue, QString *errorMessage)
+{
+    QList<WatchData> dumperResult;
+    WatchItem *dumpedItem = *ptrToDumpedItem;
+    const CdbDumperHelper::DumpResult rc = dumper->dumpType(wd, true, dumperOwnerValue, &dumperResult, errorMessage);
+    if (debugCDBWatchHandling > 1)
+        qDebug() << "dumper for " << wd.type << " returns " << rc;
+
+    switch (rc) {
+    case CdbDumperHelper::DumpError:
+        return rc;
+    case CdbDumperHelper::DumpNotHandled:
+        errorMessage->clear();
+        return rc;
+    case CdbDumperHelper::DumpOk:
+        break;
+    }
+    // Dumpers omit types for complicated templates
+    fixDumperResult(wd, &dumperResult);
+    // Discard the original item and insert the dumper results
+    if (dumpedItem) {
+        dumpedItem->setData(dumperResult.front());
+    } else {
+        dumpedItem = new WatchItem(dumperResult.front());
+        *ptrToDumpedItem = dumpedItem;
+    }
+    dumperResult.pop_front();
+    foreach(const WatchData &dwd, dumperResult)
+        dumpedItem->addChild(new WatchItem(dwd));
+    return rc;
+}
+
+// Is this a non-null pointer to a dumpeable item with a value
+// "0x4343 class QString *" ? - Insert a fake '*' dereferenced item
+// and run dumpers on it. If that succeeds, insert the fake items owned by dumpers,
+// Note that the symbol context does not create '*' dereferenced items for
+// classes (see note in its header documentation).
+static bool expandPointerToDumpable(const QSharedPointer<CdbDumperHelper> dumper,
+                                    const WatchData &wd,
+                                    WatchItem *parent,
+                                    QString *errorMessage)
+{
+
+
+    if (debugCDBWatchHandling > 1)
+        qDebug() << ">expandPointerToDumpable" << wd.iname;
+
+    WatchItem *derefedWdItem = 0;
+    WatchItem *ptrWd = 0;
+    bool handled = false;
+    do {
+        if (!isPointerType(wd.type))
+            break;
+        const int classPos = wd.value.indexOf(" class ");
+        if (classPos == -1)
+            break;
+        const QString hexAddrS = wd.value.mid(0, classPos);
+        static const QRegExp hexNullPattern(QLatin1String("0x0+"));
+        Q_ASSERT(hexNullPattern.isValid());
+        if (hexNullPattern.exactMatch(hexAddrS))
+            break;
+        const QString type = stripPointerType(wd.value.mid(classPos + 7));
+        WatchData derefedWd;
+        derefedWd.setType(type);
+        derefedWd.setAddress(hexAddrS);
+        derefedWd.name = QString(QLatin1Char('*'));
+        derefedWd.iname = wd.iname + QLatin1String(".*");
+        derefedWd.source = LocalsOwnerDumper;
+        QList<WatchData> dumperResult;
+        const CdbDumperHelper::DumpResult dr = dumpItem(dumper, derefedWd, &derefedWdItem, LocalsOwnerDumper, errorMessage);
+        if (dr != CdbDumperHelper::DumpOk)
+            break;
+        // Insert the pointer item with 1 additional child + its dumper results
+        // Note: formal arguments might already be expanded in the symbol group.
+        ptrWd = new WatchItem(wd);
+        ptrWd->source = LocalsOwnerDumper;
+        ptrWd->setHasChildren(true);
+        ptrWd->setChildrenUnneeded();
+        ptrWd->addChild(derefedWdItem);
+        parent->addChild(ptrWd);
+        handled = true;
+    } while (false);
+    if (!handled) {
+        delete derefedWdItem;
+        delete ptrWd;
+    }
+    if (debugCDBWatchHandling > 1)
+        qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage;
+    return handled;
+}
+
+// Main routine for inserting an item from the symbol group using the dumpers
+// where applicable.
+static inline bool insertDumpedItem(const QSharedPointer<CdbDumperHelper> &dumper,
+                                    const WatchData &wd,
+                                    WatchItem *parent,
+                                    QString *errorMessage)
+{
+    if (debugCDBWatchHandling > 1)
+        qDebug() << "insertItem=" << wd.toString();
+    // Check pointer to dumpeable, dumpeable, insert accordingly.
+    if (expandPointerToDumpable(dumper, wd, parent, errorMessage))
+        return true;
+    WatchItem *dumpedItem = 0;
+    // Add item owned by Dumper or symbol group on failure.
+    const CdbDumperHelper::DumpResult dr = dumpItem(dumper, wd, &dumpedItem, LocalsOwnerDumper, errorMessage);
+    switch (dr) {
+        case CdbDumperHelper::DumpOk:
+        dumpedItem->parent = parent;
+        parent->children.push_back(dumpedItem);
+        break;
+    case CdbDumperHelper::DumpNotHandled:
+        case CdbDumperHelper::DumpError: {
+        WatchItem *symbolItem = new WatchItem(wd);
+        symbolItem->source = LocalsOwnerSymbolGroup;
+        parent->addChild(symbolItem);
+    }
+        break;
+    }
+    return true;
+}
+
+// Helper to add a sequence of  WatchData from the symbol group context to an item.
+// Checks if the item is dumpable in some way; if so, dump it and use that instead of
+// symbol group.
+class SymbolGroupDumperInserter
+{
+public:
+    explicit SymbolGroupDumperInserter(const QSharedPointer<CdbDumperHelper> &dumper,
+                                       WatchItem *parent,
+                                       QString *errorMessage);
+
+    inline SymbolGroupDumperInserter& operator*() { return *this; }
+    inline SymbolGroupDumperInserter&operator=(const WatchData &wd) {
+        insertDumpedItem(m_dumper, wd, m_parent, m_errorMessage);
+        return *this;
+    }
+    inline SymbolGroupDumperInserter&operator++() { return *this; }
+
+private:
+    const QSharedPointer<CdbDumperHelper> m_dumper;
+    WatchItem *m_parent;
+    QString *m_errorMessage;
+};
+
+SymbolGroupDumperInserter::SymbolGroupDumperInserter(const QSharedPointer<CdbDumperHelper> &dumper,
+                                              WatchItem *parent,
+                                              QString *errorMessage) :
+    m_dumper(dumper),
+    m_parent(parent),
+    m_errorMessage(errorMessage)
+{
+}
+
+// -------------- CdbLocalsModel
+
+CdbLocalsModel::CdbLocalsModel(const QSharedPointer<CdbDumperHelper> &dh,
+                               WatchHandler *handler, WatchType type, QObject *parent) :
+    AbstractSyncWatchModel(handler, type, parent),
+    m_dumperHelper(dh),
+    m_symbolGroupContext(0),
+    m_useDumpers(false)
+{
+}
+
+CdbLocalsModel::~CdbLocalsModel()
+{
+}
+
+bool CdbLocalsModel::fetchChildren(WatchItem *wd, QString *errorMessage)
+{
+    if (!m_symbolGroupContext) {
+        *errorMessage = msgNoStackFrame();
+        return false;
+    }
+    if (debugCDBWatchHandling)
+        qDebug() << "fetchChildren" << wd->iname;
+
+    // Check the owner and call it to expand the item.
+    switch (wd->source) {
+    case LocalsOwnerSymbolGroup:
+        if (m_useDumpers && m_dumperHelper->isEnabled()) {
+            SymbolGroupDumperInserter inserter(m_dumperHelper, wd, errorMessage);
+            return m_symbolGroupContext->getChildSymbols(wd->iname, inserter, errorMessage);
+        } else {
+            return m_symbolGroupContext->getChildSymbols(wd->iname, SymbolGroupInserter(wd), errorMessage);
+        }
+        break;
+    case LocalsOwnerDumper:
+        if (dumpItem(m_dumperHelper, *wd, &wd, LocalsOwnerDumper, errorMessage) == CdbDumperHelper::DumpOk)
+            return true;
+        if (wd->isValueNeeded())
+            wd->setValue(msgUnknown());
+        qWarning("%s: No value for %s\n", Q_FUNC_INFO, qPrintable(wd->toString()));
+        return false;
+    }
+    return false;
+}
+
+bool CdbLocalsModel::complete(WatchItem *wd, QString *errorMessage)
+{
+    if (!m_symbolGroupContext) {
+        *errorMessage = msgNoStackFrame();
+        return false;
+    }
+    if (debugCDBWatchHandling)
+        qDebug() << "complete" << wd->iname;
+    // Might as well fetch children when completing a dumped item.
+    return fetchChildren(wd, errorMessage);
+}
+
+void CdbLocalsModel::setSymbolGroupContext(CdbSymbolGroupContext *s)
+{
+    if (s == m_symbolGroupContext)
+        return;
+    if (debugCDBWatchHandling)
+        qDebug() << ">setSymbolGroupContext" << s;
+    m_symbolGroupContext = s;
+    reinitialize();
+    if (!m_symbolGroupContext)
+        return;
+    // Populate first row
+    WatchItem *item = dummyRoot();
+    QString errorMessage;
+    do {
+        if (m_useDumpers && m_dumperHelper->isEnabled()) {
+            SymbolGroupDumperInserter inserter(m_dumperHelper, item, &errorMessage);
+            if (!m_symbolGroupContext->getChildSymbols(m_symbolGroupContext->prefix(), inserter, &errorMessage)) {
+                break;
+            }
+        } else {
+            if (!m_symbolGroupContext->getChildSymbols(m_symbolGroupContext->prefix(), SymbolGroupInserter(item), &errorMessage))
+                break;
+        }
+        if (item->children.empty())
+            break;
+        reset();
+    } while (false);
+    if (!errorMessage.isEmpty())
+        emit error(errorMessage);
+    if (debugCDBWatchHandling)
+        qDebug() << "<setSymbolGroupContext" << item->children.size() << errorMessage;
+    if (debugCDBWatchHandling > 1)
+        qDebug() << '\n' << *this;
+}
+
+// ---- CdbWatchModel
+
+enum { WatchOwnerNewItem, WatchOwnerExpression, WatchOwnerDumper };
+
+CdbWatchModel::CdbWatchModel(CdbDebugEngine *engine,
+                             const QSharedPointer<CdbDumperHelper> &dh,
+                             WatchHandler *handler,
+                             WatchType type, QObject *parent) :
+   AbstractSyncWatchModel(handler, type, parent),
+   m_dumperHelper(dh),
+   m_engine(engine)
+{
+}
+
+CdbWatchModel::~CdbWatchModel()
+{
+}
+
+bool CdbWatchModel::evaluateWatchExpression(WatchData *wd, QString *errorMessage)
+{
+    QString value;
+    QString type;
+
+    const bool rc = m_engine->evaluateExpression(wd->exp, &value, &type, errorMessage);
+    if (!rc) {
+        wd->setValue(msgUnknown());
+        return false;
+    }
+    wd->setValue(value);
+    wd->setType(type);
+    return true;
+}
+
+bool CdbWatchModel::fetchChildren(WatchItem *wd, QString *errorMessage)
+{
+    if (debugCDBWatchHandling)
+        qDebug() << "Watch:fetchChildren" << wd->iname << wd->source;
+    // We need to be halted.
+    if (!m_engine->isDebuggeeHalted()) {
+        *errorMessage = QCoreApplication::translate("CdbWatchModels", "Can evaluates watches only in halted state.");
+        wd->setValue(msgUnknown());
+        return false;
+    }
+    // New item with address -> dumper.
+    if (wd->source == WatchOwnerNewItem)
+        wd->source = wd->addr.isEmpty() ? WatchOwnerExpression : WatchOwnerDumper;
+    // Expressions
+    if (wd->source == WatchOwnerExpression)
+        return evaluateWatchExpression(wd, errorMessage);
+    // Variables by address
+    if (!m_dumperHelper->isEnabled() || !theDebuggerBoolSetting(UseDebuggingHelpers)) {
+        *errorMessage = QCoreApplication::translate("CdbWatchModels", "Cannot evaluate '%1' due to dumpers being disabled.").arg(wd->name);
+        return false;
+    }
+    return dumpItem(m_dumperHelper, *wd, &wd, WatchOwnerDumper, errorMessage) == CdbDumperHelper::DumpOk;
+}
+
+bool CdbWatchModel::complete(WatchItem *wd, QString *errorMessage)
+{
+    return fetchChildren(wd, errorMessage);
+}
+
+void CdbWatchModel::addWatcher(const WatchData &wd)
+{
+    WatchItem *root = dummyRoot();
+    if (!root)
+        return;
+    const QModelIndex rootIndex = watchIndex(root);
+    beginInsertRows(rootIndex, root->children.size(), root->children.size());
+    root->addChild(new WatchItem(wd));
+    endInsertRows();
+}
+
+void CdbWatchModel::refresh()
+{
+    // Refresh data for a new break
+    WatchItem *root = dummyRoot();
+    if (!root)
+        return;
+    const int childCount = root->children.size();
+    if (!childCount)
+        return;
+    // reset flags, if there children, trigger a reset
+    bool resetRequired = false;
+    for (int i = 0; i < childCount; i++) {
+        WatchItem *topLevel = root->children.at(i);
+        topLevel->setAllNeeded();
+        if (!topLevel->children.empty()) {
+            topLevel->removeChildren();
+            resetRequired = true;
+        }
+    }
+    // notify model
+    if (resetRequired) {
+        reset();
+    } else {
+        const QModelIndex topLeft = watchIndex(root->children.front());
+        const QModelIndex bottomLeft = root->children.size() == 1 ? topLeft : watchIndex(root->children.back());
+        emit dataChanged(topLeft.sibling(0, 1), bottomLeft.sibling(0, columnCount() -1));
+    }
+}
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/cdb/cdbwatchmodels.h b/src/plugins/debugger/cdb/cdbwatchmodels.h
new file mode 100644
index 00000000000..29c8be80505
--- /dev/null
+++ b/src/plugins/debugger/cdb/cdbwatchmodels.h
@@ -0,0 +1,96 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+**
+**************************************************************************/
+
+#ifndef CDBWATCHMODELS_H
+#define CDBWATCHMODELS_H
+
+#include "abstractsyncwatchmodel.h"
+#include <QtCore/QSharedPointer>
+
+namespace Debugger {
+namespace Internal {
+
+class CdbSymbolGroupContext;
+class CdbDumperHelper;
+class CdbDebugEngine;
+
+class CdbLocalsModel : public AbstractSyncWatchModel {
+    Q_DISABLE_COPY(CdbLocalsModel)
+    Q_OBJECT
+public:
+    explicit CdbLocalsModel(const QSharedPointer<CdbDumperHelper> &dh,
+                            WatchHandler *handler, WatchType type, QObject *parent = 0);
+    ~CdbLocalsModel();
+
+    // Set a symbolgroup context, thus activating a stack frame.
+    // Set 0 to clear out.
+    void setSymbolGroupContext(CdbSymbolGroupContext *sg = 0);
+    CdbSymbolGroupContext *symbolGroupContext() const { return m_symbolGroupContext; }
+
+    void setUseDumpers(bool d) { m_useDumpers = d; }
+
+protected:
+    virtual bool fetchChildren(WatchItem *wd, QString *errorMessage);
+    virtual bool complete(WatchItem *wd, QString *errorMessage);
+
+private:
+    const QSharedPointer<CdbDumperHelper> m_dumperHelper;
+    CdbSymbolGroupContext *m_symbolGroupContext;
+    bool m_useDumpers;
+};
+
+class CdbWatchModel : public AbstractSyncWatchModel {
+   Q_DISABLE_COPY(CdbWatchModel)
+   Q_OBJECT
+public:
+    explicit CdbWatchModel(CdbDebugEngine *engine,
+                           const QSharedPointer<CdbDumperHelper> &dh,
+                           WatchHandler *handler,
+                           WatchType type, QObject *parent = 0);
+   ~CdbWatchModel();
+
+public slots:
+   void addWatcher(const WatchData &d);
+   void refresh();
+
+protected:
+    virtual bool fetchChildren(WatchItem *wd, QString *errorMessage);
+    virtual bool complete(WatchItem *wd, QString *errorMessage);
+
+private:
+    bool evaluateWatchExpression(WatchData *wd, QString *errorMessage);
+
+   const QSharedPointer<CdbDumperHelper> m_dumperHelper;
+   CdbDebugEngine *m_engine;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // CDBWATCHMODELS_H
diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro
index 25b7c355f07..c74dcdd01f4 100644
--- a/src/plugins/debugger/debugger.pro
+++ b/src/plugins/debugger/debugger.pro
@@ -44,6 +44,8 @@ HEADERS += \
     sourcefileswindow.h \
     threadswindow.h \
     watchhandler.h \
+    asyncwatchmodel.h \
+    abstractsyncwatchmodel.h \
     watchwindow.h \
 
 SOURCES += \
@@ -71,6 +73,8 @@ SOURCES += \
     sourcefileswindow.cpp \
     threadswindow.cpp \
     watchhandler.cpp \
+    asyncwatchmodel.cpp \
+    abstractsyncwatchmodel.cpp \
     watchwindow.cpp \
 
 FORMS += attachexternaldialog.ui \
diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp
index 15c586b5bba..67a9d93efbe 100644
--- a/src/plugins/debugger/debuggermanager.cpp
+++ b/src/plugins/debugger/debuggermanager.cpp
@@ -187,7 +187,8 @@ static IDebuggerEngine *tcfEngine = 0;
 
 DebuggerManager::DebuggerManager()
   : m_startParameters(new DebuggerStartParameters),
-    m_inferiorPid(0)
+    m_inferiorPid(0),
+    m_watchHandler(new WatchHandler)
 {
     init();
 }
@@ -231,6 +232,8 @@ void DebuggerManager::init()
     m_sourceFilesWindow = new SourceFilesWindow;
     m_threadsWindow = new ThreadsWindow;
     m_localsWindow = new WatchWindow(WatchWindow::LocalsType);
+    m_watchHandler->init(m_localsWindow);
+
     m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
     //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType);
     m_statusTimer = new QTimer(this);
@@ -321,14 +324,7 @@ void DebuggerManager::init()
     m_registerHandler = new RegisterHandler;
     registerView->setModel(m_registerHandler->model());
 
-    // Locals
-    m_watchHandler = new WatchHandler;
-    QTreeView *localsView = qobject_cast<QTreeView *>(m_localsWindow);
-    localsView->setModel(m_watchHandler->model(LocalsWatch));
-
-    // Watchers
-    QTreeView *watchersView = qobject_cast<QTreeView *>(m_watchersWindow);
-    watchersView->setModel(m_watchHandler->model(WatchersWatch));
+    // Locals/Watchers
     connect(m_watchHandler, SIGNAL(sessionValueRequested(QString,QVariant*)),
         this, SIGNAL(sessionValueRequested(QString,QVariant*)));
     connect(m_watchHandler, SIGNAL(setSessionValueRequested(QString,QVariant)),
@@ -337,12 +333,6 @@ void DebuggerManager::init()
         this, SLOT(assignValueInDebugger()), Qt::QueuedConnection);
 
     // Tooltip
-    //QTreeView *tooltipView = qobject_cast<QTreeView *>(m_tooltipWindow);
-    //tooltipView->setModel(m_watchHandler->model(TooltipsWatch));
-
-    connect(m_watchHandler, SIGNAL(watchDataUpdateNeeded(WatchData)),
-        this, SLOT(updateWatchData(WatchData)));
-
     m_continueAction = new QAction(this);
     m_continueAction->setText(tr("Continue"));
     m_continueAction->setIcon(QIcon(":/debugger/images/debugger_continue_small.png"));
@@ -471,12 +461,34 @@ void DebuggerManager::init()
     localsAndWatchers->setStretchFactor(2, 1);
     m_watchDock = createDockForWidget(localsAndWatchers);
 
+    initializeWatchModels(gdbEngine);
+
     setStatus(DebuggerProcessNotReady);
 }
 
+void DebuggerManager::initializeWatchModels(IDebuggerEngine *engine)
+{
+    if (engine == 0)
+        return;
+    WatchModel *localsModel = engine->watchModel(LocalsWatch);
+    m_watchHandler->setModel(LocalsWatch, localsModel);
+    m_localsWindow->setModel(localsModel);
+
+    WatchModel *watchModel = engine->watchModel(WatchersWatch);
+    m_watchHandler->setModel(WatchersWatch, watchModel);
+    m_watchersWindow->setModel(watchModel);
+
+    if (WatchModel *toolTipModel = engine->watchModel(TooltipsWatch)) {
+        m_watchHandler->setModel(TooltipsWatch, toolTipModel);
+    } else {
+        m_watchHandler->setModel(TooltipsWatch, 0);
+    }
+}
+
 QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTypeFlags)
 {
     QList<Core::IOptionsPage*> rc;
+    // Initialize watch models so columns show up initially.
     if (enabledTypeFlags & GdbEngineType)
         gdbEngine = createGdbEngine(this, &rc);
     winEngine = createWinEngine(this, (enabledTypeFlags & CdbEngineType), &rc);
@@ -487,6 +499,12 @@ QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTy
     m_engine = 0;
     if (Debugger::Constants::Internal::debug)
         qDebug() << Q_FUNC_INFO << gdbEngine << winEngine << scriptEngine << rc.size();
+    if (gdbEngine) {
+        initializeWatchModels(gdbEngine);
+    } else {
+        if (winEngine)
+            initializeWatchModels(winEngine);
+    }
     return rc;
 }
 
@@ -802,12 +820,6 @@ void DebuggerManager::setToolTipExpression(const QPoint &mousePos, TextEditor::I
         m_engine->setToolTipExpression(mousePos, editor, cursorPos);
 }
 
-void DebuggerManager::updateWatchData(const WatchData &data)
-{
-    if (m_engine)
-        m_engine->updateWatchData(data);
-}
-
 QVariant DebuggerManager::sessionValue(const QString &name)
 {
     // this is answered by the plugin
@@ -862,7 +874,7 @@ static IDebuggerEngine *determineDebuggerEngine(const QString &executable,
         return scriptEngine;
     }
 
-#ifndef Q_OS_WIN    
+#ifndef Q_OS_WIN
     Q_UNUSED(settingsIdHint)
     if (!gdbEngine) {
         *errorMessage = msgEngineNotAvailable("Gdb Engine");
@@ -943,6 +955,7 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl,
         break;
     }
 
+
     if (!m_engine) {
         debuggingFinished();
         // Create Message box with possibility to go to settings
@@ -956,6 +969,7 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl,
             Core::ICore::instance()->showOptionsDialog(_(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint);
         return;
     }
+    initializeWatchModels(m_engine);
 
     if (Debugger::Constants::Internal::debug)
         qDebug() << m_startParameters->executable << m_engine;
diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h
index c153465c884..3dc8dcb16b9 100644
--- a/src/plugins/debugger/debuggermanager.h
+++ b/src/plugins/debugger/debuggermanager.h
@@ -46,6 +46,7 @@ class QModelIndex;
 class QPoint;
 class QTimer;
 class QWidget;
+class QTreeView;
 class QDebug;
 QT_END_NAMESPACE
 
@@ -307,7 +308,6 @@ public slots:
     void detachDebugger();
 
     void addToWatchWindow();
-    void updateWatchData(const WatchData &data);
 
     void sessionLoaded();
     void sessionUnloaded();
@@ -418,6 +418,7 @@ public:
 private:
     void init();
     void runTest(const QString &fileName);
+    void initializeWatchModels(IDebuggerEngine *engine);
     QDockWidget *createDockForWidget(QWidget *widget);
     Q_SLOT void createNewDock(QWidget *widget);
     void updateDockWidget(QDockWidget *dockWidget);
@@ -483,13 +484,12 @@ private:
 
     QWidget *m_breakWindow;
     QWidget *m_disassemblerWindow;
-    QWidget *m_localsWindow;
+    QTreeView *m_localsWindow;
     QWidget *m_registerWindow;
     QWidget *m_modulesWindow;
-    //QWidget *m_tooltipWindow;
     QWidget *m_stackWindow;
     QWidget *m_threadsWindow;
-    QWidget *m_watchersWindow;
+    QTreeView *m_watchersWindow;
     DebuggerOutputWindow *m_outputWindow;
 
     int m_status;
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 3b5431012aa..d60877b4dc4 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -46,6 +46,7 @@
 #include "registerhandler.h"
 #include "stackhandler.h"
 #include "watchhandler.h"
+#include "asyncwatchmodel.h"
 #include "sourcefileswindow.h"
 
 #include "debuggerdialogs.h"
@@ -152,7 +153,8 @@ GdbEngine::GdbEngine(DebuggerManager *parent) :
     m_dumperInjectionLoad(false),
 #endif
     q(parent),
-    qq(parent->engineInterface())
+    qq(parent->engineInterface()),
+    m_models(qq->watchHandler())
 {
     m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
 #ifdef Q_OS_UNIX
@@ -170,6 +172,8 @@ GdbEngine::~GdbEngine()
 
 void GdbEngine::initializeConnections()
 {
+    connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData)));
+    connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData)));
     // Gdb Process interaction
     connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
         this, SLOT(gdbProcError(QProcess::ProcessError)));
@@ -2696,10 +2700,10 @@ static QString tooltipINameForExpression(const QString &exp)
 bool GdbEngine::showToolTip()
 {
     WatchHandler *handler = qq->watchHandler();
-    WatchModel *model = handler->model(TooltipsWatch);
+    AsyncWatchModel *model = static_cast<AsyncWatchModel *>(handler->model(TooltipsWatch));
     QString iname = tooltipINameForExpression(m_toolTipExpression);
     model->setActiveData(iname);
-    WatchItem *item = model->findItem(iname, model->dummyRoot());
+    WatchItem *item = model->findItemByIName(iname, model->dummyRoot());
     if (!item) {
         hideDebuggerToolTip();
         return false;
@@ -2788,7 +2792,7 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos,
     toolTip.name = exp;
     toolTip.iname = tooltipINameForExpression(exp);
     qq->watchHandler()->removeData(toolTip.iname);
-    qq->watchHandler()->insertData(toolTip);
+    m_models.insertData(toolTip);
 }
 
 
@@ -3146,7 +3150,7 @@ void GdbEngine::rebuildModel()
     PENDING_DEBUG("REBUILDING MODEL");
     emit gdbInputAvailable(LogStatus, _("<Rebuild Watchmodel>"));
     q->showStatusMessage(tr("Finished retrieving data."), 400);
-    qq->watchHandler()->endCycle();
+    m_models.endCycle();
     showToolTip();
 }
 
@@ -3536,7 +3540,7 @@ void GdbEngine::updateLocals()
     PENDING_DEBUG("\nRESET PENDING");
     //m_toolTipCache.clear();
     m_toolTipExpression.clear();
-    qq->watchHandler()->beginCycle();
+    m_models.beginCycle();
 
     QString level = QString::number(currentFrame());
     // '2' is 'list with type and value'
@@ -3590,7 +3594,7 @@ void GdbEngine::handleStackListLocals(const GdbResultRecord &record, const QVari
     locals += m_currentFunctionArgs;
 
     setLocals(locals);
-    qq->watchHandler()->updateWatchers();
+    m_models.updateWatchers();
 }
 
 void GdbEngine::setLocals(const QList<GdbMi> &locals)
@@ -3662,7 +3666,7 @@ void GdbEngine::insertData(const WatchData &data0)
         qDebug() << "BOGUS VALUE:" << data.toString();
         return;
     }
-    qq->watchHandler()->insertData(data);
+    m_models.insertData(data);
 }
 
 void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 41137705879..0ad6dc2e90b 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -34,6 +34,7 @@
 #include "gdbmi.h"
 #include "outputcollector.h"
 #include "watchutils.h"
+#include "asyncwatchmodel.h"
 
 #include <consoleprocess.h>
 
@@ -122,6 +123,8 @@ private:
     void loadAllSymbols();
     virtual QList<Symbol> moduleSymbols(const QString &moduleName);
 
+    WatchModel *watchModel(int type) const { return m_models.model(type); }
+
     Q_SLOT void setDebugDebuggingHelpers(const QVariant &on);
     Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
 
@@ -197,6 +200,7 @@ private slots:
     void stubStarted();
     void stubError(const QString &msg);
     void uploadProcError(QProcess::ProcessError error);
+    void updateWatchData(const WatchData &data);
 
 private:
     int terminationIndex(const QByteArray &buffer, int &length);
@@ -324,7 +328,6 @@ private:
     // FIXME: BaseClass. called to improve situation for a watch item
     void updateSubItem(const WatchData &data);
 
-    void updateWatchData(const WatchData &data);
     void rebuildModel();
 
     void insertData(const WatchData &data);
@@ -384,6 +387,7 @@ private:
 
     DebuggerManager * const q;
     IDebuggerManagerAccessForEngines * const qq;
+    AsyncWatchModelMixin m_models;
     // make sure to re-initialize new members in initializeVariables();
 };
 
diff --git a/src/plugins/debugger/idebuggerengine.h b/src/plugins/debugger/idebuggerengine.h
index dbbdb143143..f99e26bd869 100644
--- a/src/plugins/debugger/idebuggerengine.h
+++ b/src/plugins/debugger/idebuggerengine.h
@@ -46,6 +46,8 @@ class ITextEditor;
 namespace Debugger {
 namespace Internal {
 
+class WatchModel;
+
 class Symbol;
 class WatchData;
 struct DebuggerStartParameters;
@@ -60,14 +62,13 @@ public:
     virtual bool startDebugger(const QSharedPointer<DebuggerStartParameters> &startParameters) = 0;
     virtual void exitDebugger() = 0;
     virtual void detachDebugger() {}
-    virtual void updateWatchData(const WatchData &data) = 0;
 
     virtual void stepExec() = 0;
     virtual void stepOutExec() = 0;
     virtual void nextExec() = 0;
     virtual void stepIExec() = 0;
     virtual void nextIExec() = 0;
-    
+
     virtual void continueInferior() = 0;
     virtual void interruptInferior() = 0;
 
@@ -95,6 +96,8 @@ public:
     virtual void reloadFullStack() = 0;
 
     virtual void watchPoint(const QPoint &) {}
+
+    virtual WatchModel *watchModel(int type) const = 0;
 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp
index 28d20a24cc2..0096e3a414d 100644
--- a/src/plugins/debugger/script/scriptengine.cpp
+++ b/src/plugins/debugger/script/scriptengine.cpp
@@ -185,10 +185,13 @@ void ScriptAgent::scriptUnload(qint64 scriptId)
 //
 ///////////////////////////////////////////////////////////////////////
 
-ScriptEngine::ScriptEngine(DebuggerManager *parent)
+ScriptEngine::ScriptEngine(DebuggerManager *parent) :
+    q(parent),
+    qq(parent->engineInterface()),
+    m_models(qq->watchHandler())
 {
-    q = parent;
-    qq = parent->engineInterface();
+    connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData)));
+    connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData)));
     m_scriptEngine = new QScriptEngine(this);
     m_scriptAgent = new ScriptAgent(this, m_scriptEngine);
     m_scriptEngine->setAgent(m_scriptAgent);
@@ -572,7 +575,7 @@ void ScriptEngine::maybeBreakNow(bool byFunction)
 void ScriptEngine::updateLocals()
 {
     QScriptContext *context = m_scriptEngine->currentContext();
-    qq->watchHandler()->beginCycle();
+    m_models.beginCycle();
     //SDEBUG("UPDATE LOCALS");
 
     //
@@ -604,7 +607,7 @@ void ScriptEngine::updateLocals()
     data.iname = "local";
     data.name = "local";
     data.scriptValue = context->activationObject();
-    qq->watchHandler()->insertData(data);
+    m_models.insertData(data);
 
     // FIXME: Use an extra thread. This here is evil
     m_stopped = true;
@@ -679,7 +682,7 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
             data.setType("<unknown>");
             data.setValue("<unknown>");
         }
-        qq->watchHandler()->insertData(data);
+        m_models.insertData(data);
         return;
     }
 
@@ -697,13 +700,13 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
                 data1.setChildrenNeeded();
             else
                 data1.setChildrenUnneeded();
-            qq->watchHandler()->insertData(data1);
+            m_models.insertData(data1);
             ++numChild;
         }
         //SDEBUG("  ... CHILDREN: " << numChild);
         data.setHasChildren(numChild > 0);
         data.setChildrenUnneeded();
-        qq->watchHandler()->insertData(data);
+        m_models.insertData(data);
         return;
     }
 
@@ -716,7 +719,7 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
         }
         data.setHasChildren(numChild > 0);
         //SDEBUG("  ... CHILDCOUNT: " << numChild);
-        qq->watchHandler()->insertData(data);
+        m_models.insertData(data);
         return;
     }
 
diff --git a/src/plugins/debugger/script/scriptengine.h b/src/plugins/debugger/script/scriptengine.h
index ae4a434efc0..bd49d0e1ef1 100644
--- a/src/plugins/debugger/script/scriptengine.h
+++ b/src/plugins/debugger/script/scriptengine.h
@@ -49,6 +49,7 @@ class QScriptValue;
 QT_END_NAMESPACE
 
 #include "idebuggerengine.h"
+#include "asyncwatchmodel.h"
 
 namespace Debugger {
 namespace Internal {
@@ -107,14 +108,20 @@ private:
 
     bool supportsThreads() const { return true; }
     void maybeBreakNow(bool byFunction);
-    void updateWatchData(const WatchData &data);
+
     void updateLocals();
     void updateSubItem(const WatchData &data);
 
+    WatchModel *watchModel(int type) const { return m_models.model(type); }
+
+private slots:
+    void updateWatchData(const WatchData &data);
+
 private:
     friend class ScriptAgent;
     DebuggerManager *q;
     IDebuggerManagerAccessForEngines *qq;
+    AsyncWatchModelMixin m_models;
 
     QScriptEngine *m_scriptEngine;
     QString m_scriptContents;
diff --git a/src/plugins/debugger/tcf/tcfengine.cpp b/src/plugins/debugger/tcf/tcfengine.cpp
index b699f40954a..756f0d80289 100644
--- a/src/plugins/debugger/tcf/tcfengine.cpp
+++ b/src/plugins/debugger/tcf/tcfengine.cpp
@@ -112,11 +112,13 @@ QString TcfEngine::TcfCommand::toString() const
 //
 ///////////////////////////////////////////////////////////////////////
 
-TcfEngine::TcfEngine(DebuggerManager *parent)
+TcfEngine::TcfEngine(DebuggerManager *parent) :
+    q(parent),
+    qq(parent->engineInterface()),
+    m_models(qq->watchHandler())
 {
-    q = parent;
-    qq = parent->engineInterface();
-
+    connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData)));
+    connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData)));
     m_congestion = 0;
     m_inAir = 0;
 
diff --git a/src/plugins/debugger/tcf/tcfengine.h b/src/plugins/debugger/tcf/tcfengine.h
index ff5844c436b..e09097df959 100644
--- a/src/plugins/debugger/tcf/tcfengine.h
+++ b/src/plugins/debugger/tcf/tcfengine.h
@@ -30,6 +30,8 @@
 #ifndef DEBUGGER_TCFENGINE_H
 #define DEBUGGER_TCFENGINE_H
 
+#include "asyncwatchmodel.h"
+
 #include <QtCore/QByteArray>
 #include <QtCore/QHash>
 #include <QtCore/QMap>
@@ -112,10 +114,11 @@ private:
 
     bool supportsThreads() const { return true; }
     void maybeBreakNow(bool byFunction);
-    void updateWatchData(const WatchData &data);
     void updateLocals();
     void updateSubItem(const WatchData &data);
 
+    WatchModel *watchModel(int type) const { return m_models.model(type); }
+
     Q_SLOT void socketConnected();
     Q_SLOT void socketDisconnected();
     Q_SLOT void socketError(QAbstractSocket::SocketError);
@@ -128,6 +131,7 @@ private:
 
 private:
     Q_SLOT void startDebugging();
+    Q_SLOT void updateWatchData(const WatchData &data);
 
     typedef void (TcfEngine::*TcfCommandCallback)
         (const JsonValue &record, const QVariant &cookie);
@@ -153,7 +157,7 @@ private:
     QHash<int, TcfCommand> m_cookieForToken;
 
     QQueue<TcfCommand> m_sendQueue;
-    
+
     // timer based congestion control. does not seem to work well.
     void enqueueCommand(const TcfCommand &command);
     Q_SLOT void handleSendTimer();
@@ -166,6 +170,7 @@ private:
 
     DebuggerManager *q;
     IDebuggerManagerAccessForEngines *qq;
+    AsyncWatchModelMixin m_models;
     QTcpSocket *m_socket;
     QByteArray m_inbuffer;
     QList<QByteArray> m_services;
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index bad769c7dbb..c33f8bcc486 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -48,6 +48,7 @@
 #include <QtGui/QLabel>
 #include <QtGui/QToolTip>
 #include <QtGui/QTextEdit>
+#include <QtGui/QTreeView>
 
 #include <ctype.h>
 
@@ -67,40 +68,13 @@
 namespace Debugger {
 namespace Internal {
 
-static const QString strNotInScope =
-        QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
-
 static int watcherCounter = 0;
-static int generationCounter = 0;
-
-////////////////////////////////////////////////////////////////////
-//
-// WatchItem
-//
-////////////////////////////////////////////////////////////////////
-
-class WatchItem : public WatchData
-{
-public:
-    WatchItem() { parent = 0; fetchTriggered = false; }
-
-    WatchItem(const WatchData &data) : WatchData(data)
-        { parent = 0; fetchTriggered = false; }
-
-    void setData(const WatchData &data)
-        { static_cast<WatchData &>(*this) = data; }
-
-    WatchItem *parent;
-    bool fetchTriggered;      // children fetch has been triggered
-    QList<WatchItem *> children;  // fetched children
-};
-
 ////////////////////////////////////////////////////////////////////
 //
 // WatchData
 //
 ////////////////////////////////////////////////////////////////////
-   
+
 WatchData::WatchData() :
     hasChildren(false),
     generation(-1),
@@ -148,7 +122,7 @@ void WatchData::setValue(const QString &value0)
     // column. No need to duplicate it here.
     if (value.startsWith("(" + type + ") 0x"))
         value = value.section(" ", -1, -1);
-    
+
     setValueUnneeded();
 }
 
@@ -272,109 +246,125 @@ QString WatchData::toToolTip() const
     return res;
 }
 
-///////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
 //
-// WatchModel
+// WatchItem
 //
-///////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
 
-WatchModel::WatchModel(WatchHandler *handler, WatchType type)
-    : QAbstractItemModel(handler), m_handler(handler), m_type(type)
+WatchItem::WatchItem() :
+    parent(0),
+    fetchTriggered(false)
 {
-    m_root = new WatchItem;
-    m_root->hasChildren = 1;
-    m_root->state = 0;
-    m_root->name = WatchHandler::tr("Root");
+}
 
-    WatchItem *item = new WatchItem;
+WatchItem::WatchItem(const WatchData &data) :
+    WatchData(data),
+    parent(0),
+    fetchTriggered(false)
+{
+}
 
-    switch (m_type) {
-        case LocalsWatch:
-            item->iname = QLatin1String("local");
-            item->name = WatchHandler::tr("Locals");
-            break;
-        case WatchersWatch:
-            item->iname = QLatin1String("watch");
-            item->name = WatchHandler::tr("Watchers");
-            break;
-        case TooltipsWatch:
-            item->iname = QLatin1String("tooltip");
-            item->name = WatchHandler::tr("Tooltip");
-            break;
-    }
-    item->hasChildren = true;
-    item->state = 0;
-    item->parent = m_root;
-    item->fetchTriggered = true;
+void WatchItem::setData(const WatchData &data)
+{
+    static_cast<WatchData &>(*this) = data;
+}
 
-    m_root->children.append(item);
+WatchItem::~WatchItem()
+{
+    qDeleteAll(children);
 }
 
-WatchItem *WatchModel::dummyRoot() const
+void WatchItem::removeChildren()
 {
-    QTC_ASSERT(!m_root->children.isEmpty(), return 0);
-    return m_root->children.front();
+    if (!children.empty()) {
+        qDeleteAll(children);
+        children.clear();
+    }
 }
 
-void WatchModel::reinitialize()
+void WatchItem::addChild(WatchItem *child)
 {
-    WatchItem *item = dummyRoot();
-    QTC_ASSERT(item, return);
-    QModelIndex index = watchIndex(item);
-    int n = item->children.size();
-    if (n == 0)
-        return;
-    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname);
-    beginRemoveRows(index, 0, n - 1);
-    qDeleteAll(item->children);
-    item->children.clear();
-    endRemoveRows();
+    children.push_back(child);
+    child->parent = this;
 }
 
-void WatchModel::removeOutdated()
+///////////////////////////////////////////////////////////////////////
+//
+// WatchPredicate
+//
+///////////////////////////////////////////////////////////////////////
+
+WatchPredicate::WatchPredicate(Mode mode, const QString &pattern) :
+    m_pattern(pattern),
+    m_mode(mode)
 {
-    WatchItem *item = dummyRoot();
-    QTC_ASSERT(item, return);
-    foreach (WatchItem *child, item->children)
-        removeOutdatedHelper(child);
-#if DEBUG_MODEL
-#if USE_MODEL_TEST
-    //(void) new ModelTest(this, this);
-#endif
-#endif
 }
 
-void WatchModel::removeOutdatedHelper(WatchItem *item)
+bool WatchPredicate::operator()(const WatchData &w) const
 {
-    if (item->generation < generationCounter)
-        removeItem(item);
-    else {
-        foreach (WatchItem *child, item->children)
-            removeOutdatedHelper(child);
-        item->fetchTriggered = false;
+    switch (m_mode) {
+        case INameMatch:
+        return m_pattern == w.iname;
+        case ExpressionMatch:
+        return m_pattern == w.exp;
     }
+    return false;
 }
 
-void WatchModel::removeItem(WatchItem *item)
+///////////////////////////////////////////////////////////////////////
+//
+// WatchModel
+//
+///////////////////////////////////////////////////////////////////////
+
+WatchModel::WatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
+    QAbstractItemModel(parent),
+    m_root(new WatchItem),
+    m_handler(handler),
+    m_type(type)
 {
-    WatchItem *parent = item->parent;
-    QModelIndex index = watchIndex(parent);
-    int n = parent->children.indexOf(item);
-    //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
-    beginRemoveRows(index, n, n);
-    parent->children.removeAt(n);
-    endRemoveRows();
+    WatchItem *dummyRoot = new WatchItem;
+
+    switch (type) {
+        case LocalsWatch:
+            dummyRoot->iname = QLatin1String("local");
+            dummyRoot->name = WatchHandler::tr("Locals");
+            break;
+        case WatchersWatch:
+            dummyRoot->iname = QLatin1String("watch");
+            dummyRoot->name = WatchHandler::tr("Watchers");
+            break;
+        case TooltipsWatch:
+            dummyRoot->iname = QLatin1String("tooltip");
+            dummyRoot->name = WatchHandler::tr("Tooltip");
+            break;
+        case WatchModelCount:
+            break;
+        }
+    dummyRoot->hasChildren = true;
+    dummyRoot->state = 0;
+
+    m_root->addChild(dummyRoot);
+
+    m_root->hasChildren = 1;
+    m_root->state = 0;
+    m_root->name = WatchHandler::tr("Root");
 }
 
-static QString parentName(const QString &iname)
+WatchModel::~WatchModel()
 {
-    int pos = iname.lastIndexOf(QLatin1Char('.'));
+    delete m_root;
+}
+
+QString WatchModel::parentName(const QString &iname)
+{
+    const int pos = iname.lastIndexOf(QLatin1Char('.'));
     if (pos == -1)
         return QString();
     return iname.left(pos);
 }
 
-
 static QString chopConst(QString type)
 {
    while (1) {
@@ -419,7 +409,7 @@ QString niceType(const QString typeIn)
     for (int i = 0; i < 10; ++i) {
         int start = type.indexOf("std::allocator<");
         if (start == -1)
-            break; 
+            break;
         // search for matching '>'
         int pos;
         int level = 0;
@@ -527,51 +517,10 @@ static QString formattedValue(const WatchData &data,
     return data.value;
 }
 
-bool WatchModel::canFetchMore(const QModelIndex &index) const
-{
-    return index.isValid() && !watchItem(index)->fetchTriggered;
-}
-
-void WatchModel::fetchMore(const QModelIndex &index)
-{
-    QTC_ASSERT(index.isValid(), return);
-    QTC_ASSERT(!watchItem(index)->fetchTriggered, return);
-    if (WatchItem *item = watchItem(index)) {
-        item->fetchTriggered = true;
-        WatchData data = *item;
-        data.setChildrenNeeded();
-        emit m_handler->watchDataUpdateNeeded(data);
-    }
-}
-
-QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
-{
-    if (!hasIndex(row, column, parent))
-        return QModelIndex();
-
-    const WatchItem *item = watchItem(parent);
-    QTC_ASSERT(item, return QModelIndex());
-    return createIndex(row, column, (void*)(item->children.at(row)));
-}
-
-QModelIndex WatchModel::parent(const QModelIndex &idx) const
+int WatchModel::columnCount(const QModelIndex &idx) const
 {
-    if (!idx.isValid())
-        return QModelIndex();
-
-    const WatchItem *item = watchItem(idx);
-    if (!item->parent || item->parent == m_root)
-        return QModelIndex();
-
-    const WatchItem *grandparent = item->parent->parent;
-    if (!grandparent)
-        return QModelIndex();
-
-    for (int i = 0; i < grandparent->children.size(); ++i)
-        if (grandparent->children.at(i) == item->parent)
-            return createIndex(i, 0, (void*) item->parent);
-
-    return QModelIndex();
+    Q_UNUSED(idx);
+    return 3;
 }
 
 int WatchModel::rowCount(const QModelIndex &idx) const
@@ -583,45 +532,18 @@ int WatchModel::rowCount(const QModelIndex &idx) const
     return watchItem(idx)->children.size();
 }
 
-int WatchModel::columnCount(const QModelIndex &idx) const
-{
-    Q_UNUSED(idx);
-    return 3;
-}
-
-bool WatchModel::hasChildren(const QModelIndex &parent) const
-{
-    WatchItem *item = watchItem(parent);
-    return !item || item->hasChildren;
-}
-
-WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
+bool WatchModel::hasChildren(const QModelIndex &idx) const
 {
-    return idx.isValid() 
-        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
-}
-
-QModelIndex WatchModel::watchIndex(const WatchItem *item) const
-{
-    return watchIndexHelper(item, m_root, QModelIndex());
-}
-
-QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle, 
-    const WatchItem *parentItem, const QModelIndex &parentIndex) const
-{
-    if (needle == parentItem)
-        return parentIndex;
-    for (int i = parentItem->children.size(); --i >= 0; ) {
-        const WatchItem *childItem = parentItem->children.at(i);
-        QModelIndex childIndex = index(i, 0, parentIndex);
-        QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
-        if (idx.isValid())
-            return idx;
+    if (const WatchItem *item = watchItem(idx)) {
+        if (!item->children.empty())
+            return true;
+        QTC_ASSERT(item->isHasChildrenKnown(), return false);
+        return item->hasChildren;
     }
-    return QModelIndex();
+    return false;
 }
 
-void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex) 
+void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
 {
     QModelIndex idx1 = index(0, column, parentIndex);
     QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex);
@@ -635,11 +557,16 @@ void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
 
 QVariant WatchModel::data(const QModelIndex &idx, int role) const
 {
-    const WatchItem &data = *watchItem(idx);
+    if (const WatchData *wdata = watchItem(idx))
+        return data(*wdata, idx.column(), role);
+    return QVariant();
+}
 
+QVariant WatchModel::data(const WatchData &data, int column, int role) const
+{
     switch (role) {
         case Qt::DisplayRole: {
-            switch (idx.column()) {
+            switch (column) {
                 case 0: return data.name;
                 case 1: return formattedValue(data,
                     m_handler->m_individualFormats[data.iname],
@@ -657,7 +584,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
             static const QVariant red(QColor(200, 0, 0));
             static const QVariant black(QColor(0, 0, 0));
             static const QVariant gray(QColor(140, 140, 140));
-            switch (idx.column()) {
+            switch (column) {
                 case 0: return black;
                 case 1: return data.valuedisabled ? gray : data.changed ? red : black;
                 case 2: return black;
@@ -678,7 +605,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
         case ActiveDataRole:
             qDebug() << "ASK FOR" << data.iname;
             return true;
-   
+
         case TypeFormatListRole:
             if (isIntType(data.type))
                 return QStringList() << tr("decimal") << tr("hexadecimal")
@@ -696,14 +623,14 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
         }
 
         default:
-            break; 
+            break;
     }
     return QVariant();
 }
 
 bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
 {
-    WatchItem &data = *watchItem(index);
+    WatchData &data = *watchItem(index);
     if (role == ExpandedRole) {
         if (value.toBool())
             m_handler->m_expandedINames.insert(data.iname);
@@ -760,75 +687,162 @@ QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int ro
             case 2: return QString(tr("Type")  + QLatin1String("     "));
         }
     }
-    return QVariant(); 
+    return QVariant();
+}
+
+void WatchModel::setValueByIName(const QString &iname, const QString &value)
+{
+    if (WatchItem *wd = findItemByIName(iname, dummyRoot())) {
+        if (wd->value != value) {
+            wd->setValue(value);
+            const QModelIndex index = watchIndex(wd);
+            emit dataChanged(index.sibling(0, 1), index.sibling(0, 2));
+        }
+    }
 }
 
-static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
+void WatchModel::setValueByExpression(const QString &exp, const QString &value)
 {
-    QString name1 = item1->iname.section('.', -1);
-    QString name2 = item2->iname.section('.', -1);
-    if (!name1.isEmpty() && !name2.isEmpty()) {
-        if (name1.at(0).isDigit() && name2.at(0).isDigit())
-            return name1.toInt() < name2.toInt();
+    if (WatchItem *wd = findItemByExpression(exp, dummyRoot())) {
+        if (wd->value != value) {
+            wd->setValue(value);
+            const QModelIndex index = watchIndex(wd);
+            emit dataChanged(index.sibling(0, 1), index.sibling(0, 2));
+        }
     }
-    return name1 < name2; 
 }
 
-static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
+WatchItem *WatchModel::root() const
 {
-    QList<WatchItem *>::const_iterator it =
-        qLowerBound(list.begin(), list.end(), item, iNameSorter);
-    return it - list.begin(); 
+    return m_root;
 }
 
-void WatchModel::insertData(const WatchData &data)
+WatchItem *WatchModel::dummyRoot() const
+{
+    if (!m_root->children.isEmpty())
+        return m_root->children.front();
+    return 0;
+}
+
+void WatchModel::reinitialize()
 {
-    // qDebug() << "WMI:" << data.toString();
-    QTC_ASSERT(!data.iname.isEmpty(), return);
-    WatchItem *parent = findItem(parentName(data.iname), m_root);
-    if (!parent) {
-        WatchData parent;
-        parent.iname = parentName(data.iname);
-        insertData(parent);
-        //MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
+    WatchItem *item = dummyRoot();
+    QTC_ASSERT(item, return);
+    const QModelIndex index = watchIndex(item);
+    const int n = item->children.size();
+    if (n == 0)
         return;
+    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname);
+    beginRemoveRows(index, 0, n - 1);
+    item->removeChildren();
+    endRemoveRows();
+}
+
+void WatchModel::removeItem(WatchItem *itemIn)
+{
+    WatchItem *item = static_cast<WatchItem *>(itemIn);
+    WatchItem *parent = item->parent;
+    const QModelIndex index = watchIndex(parent);
+    const int n = parent->children.indexOf(item);
+    //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
+    beginRemoveRows(index, n, n);
+    parent->children.removeAt(n);
+    endRemoveRows();
+}
+
+QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
+{
+    if (!hasIndex(row, column, parent))
+        return QModelIndex();
+
+    const WatchItem *item = watchItem(parent);
+    QTC_ASSERT(item, return QModelIndex());
+    return createIndex(row, column, (void*)(item->children.at(row)));
+}
+
+QModelIndex WatchModel::parent(const QModelIndex &idx) const
+{
+    if (!idx.isValid())
+        return QModelIndex();
+
+    const WatchItem *item = static_cast<WatchItem *>(watchItem(idx));
+    if (!item->parent || item->parent == m_root)
+        return QModelIndex();
+
+    const WatchItem *grandparent = item->parent->parent;
+    if (!grandparent)
+        return QModelIndex();
+
+    for (int i = 0; i < grandparent->children.size(); ++i)
+        if (grandparent->children.at(i) == item->parent)
+            return createIndex(i, 0, (void*) item->parent);
+
+    return QModelIndex();
+}
+
+WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
+
+{
+    return idx.isValid()
+            ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
+}
+
+QModelIndex WatchModel::watchIndex(const WatchItem *item) const
+{
+    return watchIndexHelper(item, m_root, QModelIndex());
+}
+
+QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
+    const WatchItem *parentItem, const QModelIndex &parentIndex) const
+{
+    if (needle == parentItem)
+        return parentIndex;
+    for (int i = parentItem->children.size(); --i >= 0; ) {
+        const WatchItem *childItem = parentItem->children.at(i);
+        QModelIndex childIndex = index(i, 0, parentIndex);
+        QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
+        if (idx.isValid())
+            return idx;
     }
-    QModelIndex index = watchIndex(parent);
-    if (WatchItem *oldItem = findItem(data.iname, parent)) {
-        // overwrite old entry
-        //MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
-        bool changed = !data.value.isEmpty()
-            && data.value != oldItem->value
-            && data.value != strNotInScope;
-        oldItem->setData(data);
-        oldItem->changed = changed;
-        oldItem->generation = generationCounter;
-        QModelIndex idx = watchIndex(oldItem);
-        emit dataChanged(idx, idx.sibling(idx.row(), 2));
-    } else {
-        // add new entry
-        //MODEL_DEBUG("INSERT : " << data.iname << data.value);
-        WatchItem *item = new WatchItem(data);
-        item->parent = parent;
-        item->generation = generationCounter;
-        item->changed = true;
-        int n = findInsertPosition(parent->children, item);
-        beginInsertRows(index, n, n);
-        parent->children.insert(n, item);
-        endInsertRows();
-    }
+    return QModelIndex();
 }
 
-WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
+static WatchItem *findRecursion(WatchItem *root, const WatchPredicate &p)
 {
-    if (root->iname == iname)
+    if (p(*root))
         return root;
     for (int i = root->children.size(); --i >= 0; )
-        if (WatchItem *item = findItem(iname, root->children.at(i)))
+        if (WatchItem *item = findRecursion(root->children.at(i), p))
             return item;
     return 0;
 }
 
+WatchItem *WatchModel::findItemByExpression(const QString &exp, WatchItem *root) const
+{
+    return findRecursion(root, WatchPredicate(WatchPredicate::ExpressionMatch, exp));
+}
+
+WatchItem *WatchModel::findItemByIName(const QString &iname, WatchItem *root) const
+{
+    return findRecursion(root, WatchPredicate(WatchPredicate::INameMatch, iname));
+}
+
+static void debugItemRecursion(QDebug d, WatchItem *item, QString indent = QString())
+{
+    qDebug() << (indent + item->toString());
+    if (!item->children.isEmpty()) {
+        indent += QLatin1String("   ");
+        foreach(WatchItem *c, item->children)
+            debugItemRecursion(d, c, indent);
+    }
+}
+
+QDebug operator<<(QDebug d, const WatchModel &wm)
+{
+    if (WatchItem *root = wm.dummyRoot())
+        debugItemRecursion(d.nospace(), root);
+    return d;
+}
 
 ///////////////////////////////////////////////////////////////////////
 //
@@ -836,34 +850,25 @@ WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
 //
 ///////////////////////////////////////////////////////////////////////
 
-WatchHandler::WatchHandler()
+WatchHandler::WatchHandler() :
+    m_expandPointers(true),
+    m_inChange(false)
 {
-    m_expandPointers = true;
-    m_inChange = false;
-
-    m_locals = new WatchModel(this, LocalsWatch);
-    m_watchers = new WatchModel(this, WatchersWatch);
-    m_tooltips = new WatchModel(this, TooltipsWatch);
-
     connect(theDebuggerAction(WatchExpression),
         SIGNAL(triggered()), this, SLOT(watchExpression()));
     connect(theDebuggerAction(RemoveWatchExpression),
         SIGNAL(triggered()), this, SLOT(removeWatchExpression()));
-}
-
-void WatchHandler::endCycle()
-{
-    m_locals->removeOutdated();
-    m_watchers->removeOutdated();
-    m_tooltips->removeOutdated();
+    qFill(m_models, m_models + WatchModelCount, static_cast<WatchModel*>(0));
 }
 
 void WatchHandler::cleanup()
 {
     m_expandedINames.clear();
     m_displayedINames.clear();
-    m_locals->reinitialize();
-    m_tooltips->reinitialize();
+    if (m_models[LocalsWatch])
+        m_models[LocalsWatch]->reinitialize();
+    if (m_models[TooltipsWatch])
+        m_models[TooltipsWatch]->reinitialize();
 #if 0
     for (EditWindows::ConstIterator it = m_editWindows.begin();
             it != m_editWindows.end(); ++it) {
@@ -874,26 +879,12 @@ void WatchHandler::cleanup()
 #endif
 }
 
-void WatchHandler::insertData(const WatchData &data)
-{
-    MODEL_DEBUG("INSERTDATA: " << data.toString());
-    QTC_ASSERT(data.isValid(), return);
-    if (data.isSomethingNeeded()) {
-        emit watchDataUpdateNeeded(data);
-    } else {
-        WatchModel *model = modelForIName(data.iname);
-        QTC_ASSERT(model, return);
-        model->insertData(data);
-    }
-}
-
 void WatchHandler::removeData(const QString &iname)
 {
     WatchModel *model = modelForIName(iname);
     if (!model)
         return;
-    WatchItem *item = model->findItem(iname, model->m_root);
-    if (item)
+    if (WatchItem *item = model->findItemByIName(iname, model->root()))
         model->removeItem(item);
 }
 
@@ -918,7 +909,7 @@ void WatchHandler::watchExpression(const QString &exp)
     if (exp.isEmpty() || exp == watcherEditPlaceHolder())
         data.setAllUnneeded();
     data.iname = watcherName(exp);
-    insertData(data);
+    emit watcherInserted(data);
     saveWatchers();
     //emit watchModelUpdateRequested();
 }
@@ -1008,33 +999,11 @@ void WatchHandler::removeWatchExpression(const QString &exp)
 {
     MODEL_DEBUG("REMOVE WATCH: " << exp);
     m_watcherNames.remove(exp);
-    foreach (WatchItem *item, m_watchers->dummyRoot()->children) {
-        if (item->exp == exp) {
-            m_watchers->removeItem(item);
+    if (WatchModel *model = m_models[WatchersWatch])
+        if (WatchItem *item = model->findItemByExpression(exp, model->root())) {
+            model->removeItem(item);
             saveWatchers();
-            break;
         }
-    }
-}
-
-void WatchHandler::beginCycle()
-{
-    ++generationCounter;
-    //m_locals->beginCycle();
-}
-
-void WatchHandler::updateWatchers()
-{
-    //qDebug() << "UPDATE WATCHERS";
-    // copy over all watchers and mark all watchers as incomplete
-    foreach (const QString &exp, m_watcherNames.keys()) {
-        WatchData data;
-        data.iname = watcherName(exp);
-        data.setAllNeeded();
-        data.name = exp;
-        data.exp = exp;
-        insertData(data);
-    }
 }
 
 void WatchHandler::loadWatchers()
@@ -1076,6 +1045,11 @@ void WatchHandler::loadTypeFormats()
     }
 }
 
+QStringList WatchHandler::watcherExpressions() const
+{
+    return m_watcherNames.keys();
+}
+
 void WatchHandler::saveTypeFormats()
 {
     QMap<QString, QVariant> typeFormats;
@@ -1099,44 +1073,61 @@ void WatchHandler::loadSessionData()
 {
     loadWatchers();
     loadTypeFormats();
+    initWatchModel();
+}
+
+void WatchHandler::initWatchModel()
+{
     foreach (const QString &exp, m_watcherNames.keys()) {
         WatchData data;
         data.iname = watcherName(exp);
         data.setAllUnneeded();
         data.name = exp;
         data.exp = exp;
-        insertData(data);
+        emit watcherInserted(data);
     }
 }
 
 WatchModel *WatchHandler::model(WatchType type) const
 {
-    switch (type) {
-        case LocalsWatch: return m_locals;
-        case WatchersWatch: return m_watchers;
-        case TooltipsWatch: return m_tooltips;
-    }
-    QTC_ASSERT(false, /**/);
-    return 0;
+    return m_models[type];
 }
-    
-WatchModel *WatchHandler::modelForIName(const QString &iname) const
+
+void WatchHandler::setModel(WatchType type, WatchModel *model)
+{
+    if (m_models[type] == model)
+        return;
+    if (type == WatchersWatch && m_models[type])
+        saveWatchers();
+    m_models[type] = model;
+    if (type == WatchersWatch)
+        initWatchModel();
+}
+
+WatchType WatchHandler::watchTypeOfIName(const QString &iname)
 {
     if (iname.startsWith(QLatin1String("local.")))
-        return m_locals;
+        return LocalsWatch;
     if (iname.startsWith(QLatin1String("watch.")))
-        return m_watchers;
+        return WatchersWatch;
     if (iname.startsWith(QLatin1String("tooltip")))
-        return m_tooltips;
-    QTC_ASSERT(false, /**/);
-    return 0;
+        return TooltipsWatch;
+    return WatchModelCount;
+}
+
+WatchModel *WatchHandler::modelForIName(const QString &iname) const
+{
+    const WatchType t = watchTypeOfIName(iname);
+    if (t == WatchModelCount)
+        return 0;
+    return m_models[t];
 }
 
 WatchData *WatchHandler::findItem(const QString &iname) const
 {
     const WatchModel *model = modelForIName(iname);
     QTC_ASSERT(model, return 0);
-    return model->findItem(iname, model->m_root);
+    return model->findItemByIName(iname, model->root());
 }
 
 QString WatchHandler::watcherEditPlaceHolder()
@@ -1149,9 +1140,55 @@ void WatchHandler::setFormat(const QString &type, int format)
 {
     m_typeFormats[type] = format;
     saveTypeFormats();
-    m_locals->emitDataChanged(1);
-    m_watchers->emitDataChanged(1);
-    m_tooltips->emitDataChanged(1);
+    for (int m = 0; m < WatchModelCount; m++)
+        m_models[m]->emitDataChanged(1);
+}
+
+void WatchHandler::init(QTreeView *localsView)
+{
+    m_localsView = localsView;
+}
+
+void WatchHandler::saveLocalsViewState(int frame)
+{
+    WatchModel *model = m_models[LocalsWatch];
+    if (!model || !m_localsView)
+        return;
+    LocalsViewStateMap::iterator it = m_localsViewState.find(frame);
+    if (it == m_localsViewState.end())
+        it = m_localsViewState.insert(frame, LocalsViewState());
+
+    const QModelIndex topIndex = m_localsView->indexAt(QPoint(0, 0));
+    it.value().firstVisibleRow = topIndex.isValid() ? topIndex.row() : 0;
+    it.value().expandedINames = m_expandedINames;
+}
+
+void WatchHandler::restoreLocalsViewState(int frame)
+{
+    WatchModel *model = m_models[LocalsWatch];
+    if (!model || !m_localsView)
+        return;
+    int firstVisibleRow = 0;
+    const LocalsViewStateMap::const_iterator it = m_localsViewState.constFind(frame);
+    if (it !=  m_localsViewState.constEnd()) {
+        firstVisibleRow = it.value().firstVisibleRow;
+        m_expandedINames = it.value().expandedINames;
+    }
+    // Loop over and expand valid inames, purge out invalid.
+    WatchItem *root = model->root();
+    for (QSet<QString>::iterator it = m_expandedINames.begin(); it != m_expandedINames.end(); ) {
+        if (const WatchItem *wd = model->findItemByIName(*it, root)) {
+            m_localsView->expand(model->watchIndex(wd));
+            ++it;
+        } else {
+            it = m_expandedINames.erase(it);
+        }
+    }
+    if (firstVisibleRow) {
+        const QModelIndex index = model->index(0, 0).child(firstVisibleRow, 0);
+        if (index.isValid())
+            m_localsView->scrollTo(index, QAbstractItemView::PositionAtTop);
+    }
 }
 
 } // namespace Internal
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index 86e7dff3b07..4b472fab3f4 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -32,19 +32,21 @@
 
 #include <QtCore/QPointer>
 #include <QtCore/QObject>
+#include <QtCore/QStringList>
 #include <QtCore/QHash>
 #include <QtCore/QSet>
-#include <QtGui/QStandardItem>
-#include <QtGui/QStandardItemModel>
-#include <QtGui/QTreeView>
+#include <QtCore/QAbstractItemModel>
 #include <QtScript/QScriptValue>
 
+QT_BEGIN_NAMESPACE
+class QTreeView;
+QT_END_NAMESPACE
+
 namespace Debugger {
 namespace Internal {
 
-class WatchItem;
 class WatchHandler;
-enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch };
+enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch, WatchModelCount };
 
 class WatchData
 {
@@ -160,51 +162,95 @@ enum DumpableFormat
     PlainFomat, 
 };
 
+class WatchHandler;
+
+// Item used by the model.
+class WatchItem : public WatchData
+{
+    Q_DISABLE_COPY(WatchItem)
+public:
+    WatchItem();
+    explicit WatchItem(const WatchData &data);
+
+    ~WatchItem();
+
+    void setData(const WatchData &data);
+    void addChild(WatchItem *child);
+    void removeChildren();
+
+    WatchItem *parent;
+    bool fetchTriggered;
+    QList<WatchItem*> children;
+};
+
+/* WatchModel: To be used by synchonous debuggers.
+ * Implements all of WatchModel and provides new virtuals for
+ * the debugger to complete items. */
+
 class WatchModel : public QAbstractItemModel
 {
     Q_OBJECT
+public:
+    explicit WatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0);
+    virtual ~WatchModel();
 
-private:
-    explicit WatchModel(WatchHandler *handler, WatchType type);
-
-    QVariant data(const QModelIndex &index, int role) const;
-    bool setData(const QModelIndex &index, const QVariant &value, int role);
-    QModelIndex index(int, int, const QModelIndex &idx) const;
-    QModelIndex parent(const QModelIndex &idx) const;
-    int rowCount(const QModelIndex &idx) const;
-    int columnCount(const QModelIndex &idx) const;
-    bool hasChildren(const QModelIndex &idx) const;
-    Qt::ItemFlags flags(const QModelIndex &idx) const;
-    QVariant headerData(int section, Qt::Orientation orientation,
-        int role = Qt::DisplayRole) const;
-    bool canFetchMore(const QModelIndex &parent) const;
-    void fetchMore(const QModelIndex &parent);
-
-    friend class WatchHandler;
-    friend class GdbEngine;
+    virtual QModelIndex index(int, int, const QModelIndex &idx = QModelIndex()) const;
+
+    virtual int columnCount(const QModelIndex &idx= QModelIndex()) const;
+    virtual int rowCount(const QModelIndex &idx) const;
+    virtual bool hasChildren(const QModelIndex &idx) const;
+    virtual QVariant data(const QModelIndex &index, int role) const;
+    virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+    virtual Qt::ItemFlags flags(const QModelIndex &idx) const;
+    virtual QVariant headerData(int section, Qt::Orientation orientation,
+                                int role = Qt::DisplayRole) const;
+
+    virtual QModelIndex parent(const QModelIndex &idx) const;
 
     WatchItem *watchItem(const QModelIndex &) const;
     QModelIndex watchIndex(const WatchItem *needle) const;
     QModelIndex watchIndexHelper(const WatchItem *needle,
         const WatchItem *parentItem, const QModelIndex &parentIndex) const;
 
-    void insertData(const WatchData &data);
-    WatchItem *findItem(const QString &iname, WatchItem *root) const;
+    WatchItem *findItemByIName(const QString &iname, WatchItem *root) const;
+    WatchItem *findItemByExpression(const QString &iname, WatchItem *root) const;
     void reinitialize();
-    void removeOutdated();
-    void removeOutdatedHelper(WatchItem *item);
-    WatchItem *dummyRoot() const;
+
     void removeItem(WatchItem *item);
-    void setActiveData(const QString &data) { m_activeData = data; }
+    WatchItem *root() const;
+
+    WatchItem *dummyRoot() const;
 
-    void emitDataChanged(int column,
-        const QModelIndex &parentIndex = QModelIndex());
+    void setValueByIName(const QString &iname, const QString &value);
+    void setValueByExpression(const QString &iname, const QString &value);
+
+    void emitDataChanged(int column, const QModelIndex &parentIndex = QModelIndex());
+
+protected:
+    WatchHandler *handler() const { return m_handler; }
+    QVariant data(const WatchData &data, int column, int role) const;
+
+    static QString parentName(const QString &iname);
 
 private:
-    WatchHandler *m_handler;
-    WatchType m_type;
     WatchItem *m_root;
-    QString m_activeData;
+    WatchHandler *m_handler;
+    const WatchType m_type;
+};
+
+QDebug operator<<(QDebug d, const WatchModel &wm);
+
+/* A helper predicate for implementing model find routines */
+class WatchPredicate {
+public:
+    enum Mode { INameMatch, ExpressionMatch };
+    explicit WatchPredicate(Mode m, const QString &pattern);
+    bool operator()(const WatchData &w) const;
+
+private:
+    const QString &m_pattern;
+    const Mode m_mode;
 };
 
 class WatchHandler : public QObject
@@ -213,21 +259,25 @@ class WatchHandler : public QObject
 
 public:
     WatchHandler();
+    void init(QTreeView *localsView);
+
     WatchModel *model(WatchType type) const;
+    static WatchType watchTypeOfIName(const QString &iname);
     WatchModel *modelForIName(const QString &data) const;
 
+    QStringList watcherExpressions() const;
+    QString watcherName(const QString &exp);
+
+    void setModel(WatchType type, WatchModel *model);
+
 //public slots:
     void cleanup();
     Q_SLOT void watchExpression(); // data in action->data().toString()
     Q_SLOT void watchExpression(const QString &exp);
     Q_SLOT void removeWatchExpression();
     Q_SLOT void removeWatchExpression(const QString &exp);
-    void beginCycle(); // called at begin of updateLocals() cycle
-    void updateWatchers(); // called after locals are fetched
-    void endCycle(); // called after all results have been received
     void showEditValue(const WatchData &data);
 
-    void insertData(const WatchData &data);
     void removeData(const QString &iname);
     WatchData *findItem(const QString &iname) const;
 
@@ -241,18 +291,32 @@ public:
     QSet<QString> expandedINames() const
         { return m_expandedINames; }
 
+    // For debuggers that clean out the locals models between frames:
+    // save/restore the expansion state.
+    Q_SLOT void restoreLocalsViewState(int frame = 0);
+    Q_SLOT void saveLocalsViewState(int frame = 0);
+
     static QString watcherEditPlaceHolder();
 
 signals:
-    void watchDataUpdateNeeded(const WatchData &data);
+    void watcherInserted(const WatchData &data);
     void sessionValueRequested(const QString &name, QVariant *value);
     void setSessionValueRequested(const QString &name, const QVariant &value);
 
 private:
-    friend class WatchModel;
+    friend class WatchModel; // needs formats, expanded inames
+
+    // Per stack-frame locals state
+    struct LocalsViewState {
+        LocalsViewState() : firstVisibleRow(0) {}
+        int firstVisibleRow;
+        QSet<QString> expandedINames;
+    };
+    typedef QMap<int, LocalsViewState> LocalsViewStateMap;
 
     void loadWatchers();
     void saveWatchers();
+    void initWatchModel();
 
     void loadTypeFormats();
     void saveTypeFormats();
@@ -265,17 +329,16 @@ private:
     EditWindows m_editWindows;
 
     QHash<QString, int> m_watcherNames;
-    QString watcherName(const QString &exp);
     QHash<QString, int> m_typeFormats;
     QHash<QString, int> m_individualFormats;
 
     void setDisplayedIName(const QString &iname, bool on);
     QSet<QString> m_expandedINames;  // those expanded in the treeview
     QSet<QString> m_displayedINames; // those with "external" viewers
+    LocalsViewStateMap m_localsViewState;
 
-    WatchModel *m_locals;
-    WatchModel *m_watchers;
-    WatchModel *m_tooltips;
+    WatchModel *m_models[WatchModelCount];
+    QPointer<QTreeView> m_localsView;
 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index 83486e899e9..7eccb04cd9e 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -554,19 +554,22 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const
             }
             wchild.setType(dchild.type.isEmpty() ? childType : dchild.type);
             wchild.setAddress(dchild.address);
-            // Child overrides.
-            const int effectiveChildChildCount = dchild.childCount == -1 ?  childChildCount : dchild.childCount;
-            switch (effectiveChildChildCount) {
+            // Child overrides. Note that WatchData::setType sets
+            // childcount = 0 for some known types.
+            if (wchild.isHasChildrenNeeded()) {
+                const int effectiveChildChildCount = dchild.childCount == -1 ?  childChildCount : dchild.childCount;
+                switch (effectiveChildChildCount) {
                 case -1:
-                wchild.setChildrenNeeded();
-                wchild.setHasChildrenNeeded();
-                break;
+                    wchild.setChildrenNeeded();
+                    wchild.setHasChildrenNeeded();
+                    break;
                 case 0:
-                wchild.setHasChildren(false);
-                break;
+                    wchild.setHasChildren(false);
+                    break;
                 default:
-                wchild.setHasChildren(true);
-                break;
+                    wchild.setHasChildren(true);
+                    break;
+                }
             }
         }
     }
diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp
index 0df432ce179..73e9c2bf0b4 100644
--- a/src/plugins/debugger/watchwindow.cpp
+++ b/src/plugins/debugger/watchwindow.cpp
@@ -327,9 +327,11 @@ void WatchWindow::editItem(const QModelIndex &idx)
     Q_UNUSED(idx); // FIXME
 }
 
-void WatchWindow::setModel(QAbstractItemModel *model)
+void WatchWindow::setModel(QAbstractItemModel *newModel)
 {
-    QTreeView::setModel(model);
+    if (model() == newModel)
+        return;
+    QTreeView::setModel(newModel);
 
     setRootIsDecorated(true);
     header()->setDefaultAlignment(Qt::AlignLeft);
@@ -337,7 +339,7 @@ void WatchWindow::setModel(QAbstractItemModel *model)
     if (m_type != LocalsType)
         header()->hide();
 
-    connect(model, SIGNAL(layoutChanged()), this, SLOT(resetHelper()));
+    connect(newModel, SIGNAL(layoutChanged()), this, SLOT(resetHelper()));
 }
 
 void WatchWindow::resetHelper()
-- 
GitLab