diff options
author | hjk <qthjk@ovi.com> | 2012-05-18 02:28:41 +0200 |
---|---|---|
committer | hjk <qthjk@ovi.com> | 2012-05-24 14:33:34 +0200 |
commit | e11a3a7697e7b432ac061277694885cf3724f25c (patch) | |
tree | 46a3140ed47b072782541d005d0edbdf1259a554 /src/plugins | |
parent | c14c1248ed01f5e16975dc1082b5ce4e58052f8b (diff) | |
download | qt-creator-e11a3a7697e7b432ac061277694885cf3724f25c.tar.gz |
debugger: rework WatchModel
It's one model for all locals, watch, return, tooltip and inspector
data. This allows more streamlined code paths and better isolation
of the model data from the WatchHandler. WatchItems are now registered
in a hash indexed by iname, so inames can be used as the primary
handle to watch data in the WatchHandler interface.
Change-Id: Idac0a808b5d785307496d1de4198a1f2e9ce3880
Reviewed-by: Aurindam Jana <aurindam.jana@nokia.com>
Diffstat (limited to 'src/plugins')
26 files changed, 895 insertions, 942 deletions
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 4b2864633a..112ed2524c 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -98,6 +98,12 @@ enum { debugSourceMapping = 0 }; enum { debugWatches = 0 }; enum { debugBreakpoints = 0 }; +enum HandleLocalsFlags +{ + PartialLocalsUpdate = 0x1, + LocalsUpdateForNewFrame = 0x2 +}; + #if 0 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line); #else @@ -550,18 +556,16 @@ bool CdbEngine::setToolTipExpression(const QPoint &mousePos, // Can this be found as a local variable? const QByteArray localsPrefix(localsPrefixC); QByteArray iname = localsPrefix + exp.toAscii(); - QModelIndex index = watchHandler()->itemIndex(iname); - if (!index.isValid()) { + if (!watchHandler()->hasItem(iname)) { // Nope, try a 'local.this.m_foo'. exp.prepend(QLatin1String("this.")); iname.insert(localsPrefix.size(), "this."); - index = watchHandler()->itemIndex(iname); - if (!index.isValid()) + if (!watchHandler()->hasItem(iname)) return false; } DebuggerToolTipWidget *tw = new DebuggerToolTipWidget; tw->setContext(context); - tw->setDebuggerModel(LocalsWatch); + tw->setDebuggerModel(LocalsType); tw->setExpression(exp); tw->acquireEngine(this); DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw); @@ -1048,7 +1052,7 @@ void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply) updateLocalVariable(item.iname); } else { item.setError(tr("Unable to add expression")); - watchHandler()->insertData(item); + watchHandler()->insertIncompleteData(item); showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3"). arg(QString::fromLatin1(item.iname), QString::fromLatin1(item.exp), QString::fromLocal8Bit(reply->errorMessage)), LogError); @@ -1086,7 +1090,10 @@ void CdbEngine::updateLocalVariable(const QByteArray &iname) str << blankSeparator << stackFrame; } str << blankSeparator << iname; - postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals); + postExtensionCommand(isWatch ? "watches" : "locals", + localsArguments, 0, + &CdbEngine::handleLocals, + 0, QVariant(int(PartialLocalsUpdate))); } bool CdbEngine::hasCapability(unsigned cap) const @@ -1465,8 +1472,7 @@ void CdbEngine::activateFrame(int index) stackHandler()->setCurrentIndex(index); const bool showAssembler = !frames.at(index).isUsable(); if (showAssembler) { // Assembly code: Clean out model and force instruction mode. - watchHandler()->beginCycle(); - watchHandler()->endCycle(); + watchHandler()->removeAllData(); QAction *assemblerAction = theAssemblerAction(); if (assemblerAction->isChecked()) { gotoLocation(frame); @@ -1485,14 +1491,12 @@ void CdbEngine::updateLocals(bool forNewStackFrame) const int frameIndex = stackHandler()->currentIndex(); if (frameIndex < 0) { - watchHandler()->beginCycle(); - watchHandler()->endCycle(); + watchHandler()->removeAllData(); return; } const StackFrame frame = stackHandler()->currentFrame(); if (!frame.isUsable()) { - watchHandler()->beginCycle(); - watchHandler()->endCycle(); + watchHandler()->removeAllData(); return; } /* Watchers: Forcibly discard old symbol group as switching from @@ -1542,9 +1546,11 @@ void CdbEngine::updateLocals(bool forNewStackFrame) } // Required arguments: frame + const int flags = forNewStackFrame ? LocalsUpdateForNewFrame : 0; str << blankSeparator << frameIndex; - watchHandler()->beginCycle(); - postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame)); + postExtensionCommand("locals", arguments, 0, + &CdbEngine::handleLocals, 0, + QVariant(flags)); } void CdbEngine::selectThread(int index) @@ -1925,6 +1931,9 @@ void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply) void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply) { + const int flags = reply->cookie.toInt(); + if (!(flags & PartialLocalsUpdate)) + watchHandler()->removeAllData(); if (reply->success) { QList<WatchData> watchData; GdbMi root; @@ -1940,16 +1949,14 @@ void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply) dummy.name = QLatin1String(child.findChild("name").data()); parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData); } - watchHandler()->insertBulkData(watchData); - watchHandler()->endCycle(); + watchHandler()->insertData(watchData); if (debugLocals) { QDebug nsp = qDebug().nospace(); nsp << "Obtained " << watchData.size() << " items:\n"; foreach (const WatchData &wd, watchData) nsp << wd.toString() <<'\n'; } - const bool forNewStackFrame = reply->cookie.toBool(); - if (forNewStackFrame) + if (flags & LocalsUpdateForNewFrame) emit stackFrameCompleted(); } else { showMessage(QString::fromLatin1(reply->errorMessage), LogWarning); diff --git a/src/plugins/debugger/debuggercore.h b/src/plugins/debugger/debuggercore.h index 897bfd677c..57d18dcd40 100644 --- a/src/plugins/debugger/debuggercore.h +++ b/src/plugins/debugger/debuggercore.h @@ -85,7 +85,7 @@ public: virtual QVariant configValue(const QString &name) const = 0; virtual void setConfigValue(const QString &name, const QVariant &value) = 0; virtual void updateState(DebuggerEngine *engine) = 0; - virtual void updateWatchersWindow() = 0; + virtual void updateWatchersWindow(bool showWatch, bool showReturn) = 0; virtual void showQtDumperLibraryWarning(const QString &details) = 0; virtual QIcon locationMarkIcon() const = 0; virtual const CPlusPlus::Snapshot &cppCodeModelSnapshot() const = 0; diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 017aa11f57..4db5bc134c 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -491,42 +491,32 @@ QAbstractItemModel *DebuggerEngine::threadsModel() const QAbstractItemModel *DebuggerEngine::localsModel() const { - QAbstractItemModel *model = watchHandler()->model(LocalsWatch); - if (model->objectName().isEmpty()) // Make debugging easier. - model->setObjectName(objectName() + QLatin1String("LocalsModel")); - return model; + return watchHandler()->model(); } QAbstractItemModel *DebuggerEngine::watchersModel() const { - QAbstractItemModel *model = watchHandler()->model(WatchersWatch); - if (model->objectName().isEmpty()) // Make debugging easier. - model->setObjectName(objectName() + QLatin1String("WatchersModel")); - return model; + return watchHandler()->model(); } QAbstractItemModel *DebuggerEngine::returnModel() const { - QAbstractItemModel *model = watchHandler()->model(ReturnWatch); - if (model->objectName().isEmpty()) // Make debugging easier. - model->setObjectName(objectName() + QLatin1String("ReturnModel")); - return model; + return watchHandler()->model(); } QAbstractItemModel *DebuggerEngine::inspectorModel() const { - QAbstractItemModel *model = watchHandler()->model(InspectWatch); - if (model->objectName().isEmpty()) // Make debugging easier. - model->setObjectName(objectName() + QLatin1String("InspectorModel")); - return model; + return watchHandler()->model(); } QAbstractItemModel *DebuggerEngine::toolTipsModel() const { - QAbstractItemModel *model = watchHandler()->model(TooltipsWatch); - if (model->objectName().isEmpty()) // Make debugging easier. - model->setObjectName(objectName() + QLatin1String("TooltipsModel")); - return model; + return watchHandler()->model(); +} + +QAbstractItemModel *DebuggerEngine::watchModel() const +{ + return watchHandler()->model(); } QAbstractItemModel *DebuggerEngine::sourceFilesModel() const diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 76089e425b..c8ff90586a 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -229,11 +229,12 @@ public: virtual QAbstractItemModel *registerModel() const; virtual QAbstractItemModel *stackModel() const; virtual QAbstractItemModel *threadsModel() const; - virtual QAbstractItemModel *localsModel() const; - virtual QAbstractItemModel *watchersModel() const; - virtual QAbstractItemModel *returnModel() const; - virtual QAbstractItemModel *inspectorModel() const; - virtual QAbstractItemModel *toolTipsModel() const; + virtual QAbstractItemModel *localsModel() const; // Deprecated, FIXME: use watchModel + virtual QAbstractItemModel *watchersModel() const; // Deprecated, FIXME: use watchModel + virtual QAbstractItemModel *returnModel() const; // Deprecated, FIXME: use watchModel + virtual QAbstractItemModel *inspectorModel() const; // Deprecated, FIXME: use watchModel + virtual QAbstractItemModel *toolTipsModel() const; // Deprecated, FIXME: use watchModel + virtual QAbstractItemModel *watchModel() const; virtual QAbstractItemModel *sourceFilesModel() const; virtual QAbstractItemModel *qtMessageLogModel() const; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index bc84112198..3efe927e5d 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -815,7 +815,7 @@ public slots: void fontSettingsChanged(const TextEditor::FontSettings &settings); void updateState(DebuggerEngine *engine); - void updateWatchersWindow(); + void updateWatchersWindow(bool showWatch, bool showReturn); void onCurrentProjectChanged(ProjectExplorer::Project *project); void sessionLoaded(); @@ -2238,12 +2238,10 @@ void DebuggerPluginPrivate::setInitialState() m_qtMessageLogWindow->setEnabled(true); } -void DebuggerPluginPrivate::updateWatchersWindow() +void DebuggerPluginPrivate::updateWatchersWindow(bool showWatch, bool showReturn) { - m_watchersWindow->setVisible( - m_watchersWindow->model()->rowCount(QModelIndex()) > 0); - m_returnWindow->setVisible( - m_returnWindow->model()->rowCount(QModelIndex()) > 0); + m_watchersWindow->setVisible(showWatch); + m_returnWindow->setVisible(showReturn); } void DebuggerPluginPrivate::updateState(DebuggerEngine *engine) @@ -2254,8 +2252,7 @@ void DebuggerPluginPrivate::updateState(DebuggerEngine *engine) QTC_ASSERT(!engine->isSlaveEngine(), return); m_threadBox->setCurrentIndex(engine->threadsHandler()->currentThread()); - - updateWatchersWindow(); + engine->watchHandler()->updateWatchersWindow(); const DebuggerState state = engine->state(); //showMessage(QString("PLUGIN SET STATE: ") diff --git a/src/plugins/debugger/debuggerstreamops.cpp b/src/plugins/debugger/debuggerstreamops.cpp index cada0ea9d3..fae28bb69c 100644 --- a/src/plugins/debugger/debuggerstreamops.cpp +++ b/src/plugins/debugger/debuggerstreamops.cpp @@ -230,7 +230,6 @@ QDataStream &operator<<(QDataStream &stream, const WatchData &wd) stream << wd.address; stream << wd.size; stream << wd.hasChildren; - stream << wd.generation; stream << wd.valueEnabled; stream << wd.valueEditable; stream << wd.error; @@ -256,7 +255,6 @@ QDataStream &operator>>(QDataStream &stream, WatchData &wd) stream >> wd.address; stream >> wd.size; stream >> wd.hasChildren; - stream >> wd.generation; stream >> wd.valueEnabled; stream >> wd.valueEditable; stream >> wd.error; diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp index ccbd610ea3..2094d29723 100644 --- a/src/plugins/debugger/debuggertooltipmanager.cpp +++ b/src/plugins/debugger/debuggertooltipmanager.cpp @@ -31,6 +31,7 @@ **************************************************************************/ #include "debuggertooltipmanager.h" +#include "debuggerinternalconstants.h" #include "watchutils.h" #include "debuggerengine.h" #include "debuggeractions.h" @@ -617,7 +618,7 @@ DebuggerToolTipWidget::DebuggerToolTipWidget(QWidget *parent) : m_titleLabel(new DraggableLabel), m_engineAcquired(false), m_creationDate(QDate::currentDate()), - m_debuggerModel(TooltipsWatch), + m_debuggerModel(TooltipType), m_treeView(new DebuggerToolTipTreeView), m_defaultModel(new QStandardItemModel(this)) { @@ -664,7 +665,7 @@ bool DebuggerToolTipWidget::matches(const QString &fileName, return function == m_context.function; } -void DebuggerToolTipWidget::acquireEngine(Debugger::DebuggerEngine *engine) +void DebuggerToolTipWidget::acquireEngine(DebuggerEngine *engine) { QTC_ASSERT(engine, return); @@ -836,7 +837,7 @@ void DebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const } /*! - \class Debugger::Internal::DebuggerToolTipExpressionFilterModel + \class Debugger::Internal::TooltipFilterModel \brief Model for tooltips filtering a local variable using the locals or tooltip model, matching on the name. @@ -847,50 +848,46 @@ void DebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const In addition, suppress the model's tooltip data to avoid a tooltip on a tooltip. */ -class DebuggerToolTipExpressionFilterModel : public QSortFilterProxyModel +class TooltipFilterModel : public QSortFilterProxyModel { public: - explicit DebuggerToolTipExpressionFilterModel(QAbstractItemModel *model, const QString &exp, QObject *parent = 0); - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + TooltipFilterModel(QAbstractItemModel *model, const QString &exp, int debuggerModel) : + m_expressions(exp.split(QLatin1Char('.'))), + m_debuggerModel(debuggerModel) + { + setSourceModel(model); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + return role == Qt::ToolTipRole + ? QVariant() : QSortFilterProxyModel::data(index, role); + } - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; private: const QStringList m_expressions; + int m_debuggerModel; }; -DebuggerToolTipExpressionFilterModel::DebuggerToolTipExpressionFilterModel(QAbstractItemModel *model, - const QString &exp, - QObject *parent) : - QSortFilterProxyModel(parent), - m_expressions(exp.split(QLatin1Char('.'))) -{ - setSourceModel(model); -} - -QVariant DebuggerToolTipExpressionFilterModel::data(const QModelIndex &index, int role) const -{ - return role != Qt::ToolTipRole ? - QSortFilterProxyModel::data(index, role) : QVariant(); -} - -// Return depth of a model index, that is, 0 for root index, 1 for level-1 children, etc. -static inline int indexDepth(QModelIndex index) -{ - int depth = 0; - for ( ; index.isValid() ; index = index.parent()) - depth++; - return depth; -} - -bool DebuggerToolTipExpressionFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +bool TooltipFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { + const QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent); + QByteArray iname = nameIndex.data(LocalsINameRole).toByteArray(); + if (m_debuggerModel == LocalsType && !iname.startsWith("local")) + return false; + if (m_debuggerModel == TooltipType && !iname.startsWith("tooltip")) + return false; // Match on expression for top level, else pass through. - const int depth = indexDepth(sourceParent); - if (depth >= m_expressions.size()) // No filters at this level + const int depth = iname.count('.'); + if (depth == 0) return true; - const QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent); - return nameIndex.data().toString() == m_expressions.at(depth); + if (depth > m_expressions.size()) + return true; + const QString name = nameIndex.data().toString(); + //const QString exp = nameIndex.data(LocalsExpressionRole).toString(); + return name == m_expressions.at(depth - 1); } /*! @@ -924,6 +921,7 @@ QAbstractItemModel *DebuggerToolTipTreeView::swapModel(QAbstractItemModel *newMo if (previousModel) previousModel->disconnect(SIGNAL(rowsInserted(QModelIndex,int,int)), this); setModel(newModel); + //setRootIndex(newModel->index(0, 0)); connect(newModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(computeSize()), Qt::QueuedConnection); computeSize(); @@ -991,24 +989,12 @@ void DebuggerToolTipTreeView::computeSize() setRootIsDecorated(rootDecorated); } -void DebuggerToolTipWidget::doAcquireEngine(Debugger::DebuggerEngine *engine) +void DebuggerToolTipWidget::doAcquireEngine(DebuggerEngine *engine) { // Create a filter model on the debugger's model and switch to it. - QAbstractItemModel *model = 0; - switch (m_debuggerModel) { - case LocalsWatch: - model = engine->localsModel(); - break; - case WatchersWatch: - model = engine->watchersModel(); - break; - case TooltipsWatch: - model = engine->toolTipsModel(); - break; - } - QTC_ASSERT(model, return); - DebuggerToolTipExpressionFilterModel *filterModel = - new DebuggerToolTipExpressionFilterModel(model, m_expression); + QAbstractItemModel *model = engine->watchModel(); + TooltipFilterModel *filterModel = + new TooltipFilterModel(model, m_expression, m_debuggerModel); swapModel(filterModel); } @@ -1311,7 +1297,7 @@ void DebuggerToolTipManager::slotUpdateVisibleToolTips() } } -void DebuggerToolTipManager::slotDebuggerStateChanged(Debugger::DebuggerState state) +void DebuggerToolTipManager::slotDebuggerStateChanged(DebuggerState state) { const QObject *engine = sender(); QTC_ASSERT(engine, return); @@ -1319,7 +1305,7 @@ void DebuggerToolTipManager::slotDebuggerStateChanged(Debugger::DebuggerState st const QString name = engine->objectName(); if (debugToolTips) qDebug() << "DebuggerToolTipWidget::debuggerStateChanged" - << engine << Debugger::DebuggerEngine::stateName(state); + << engine << DebuggerEngine::stateName(state); // Release at earliest possible convenience. switch (state) { diff --git a/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp b/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp index a9dd32c0a4..b23fffc2aa 100644 --- a/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp +++ b/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp @@ -63,8 +63,11 @@ void AbstractPlainGdbAdapter::setupInferior() QString args = startParameters().processArgs; m_engine->postCommand("-exec-arguments " + toLocalEncoding(args)); } - m_engine->postCommand("-file-exec-and-symbols \"" + execFilePath() + '"', - CB(handleFileExecAndSymbols)); + if (m_engine->gdbVersion() > 70000) + m_engine->postCommand("-file-exec-and-symbols \"" + execFilePath() + '"', + CB(handleFileExecAndSymbols)); + else + m_engine->postCommand("file " + execFilePath(), CB(handleFileExecAndSymbols)); } void AbstractPlainGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response) diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp index 3b2f82a34a..52b97a172b 100644 --- a/src/plugins/debugger/gdb/classicgdbengine.cpp +++ b/src/plugins/debugger/gdb/classicgdbengine.cpp @@ -716,7 +716,7 @@ static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents) void GdbEngine::updateLocalsClassic() { PRECONDITION; - m_pendingWatchRequests = 0; + //m_pendingWatchRequests = 0; m_pendingBreakpointRequests = 0; m_processedNames.clear(); @@ -724,15 +724,14 @@ void GdbEngine::updateLocalsClassic() qDebug() << "\nRESET PENDING"; //m_toolTipCache.clear(); clearToolTip(); - watchHandler()->beginCycle(); QByteArray level = QByteArray::number(currentFrame()); // '2' is 'list with type and value' QByteArray cmd = "-stack-list-arguments 2 " + level + ' ' + level; - postCommand(cmd, WatchUpdate, + postCommand(cmd, Discardable, CB(handleStackListArgumentsClassic)); // '2' is 'list with type and value' - postCommand("-stack-list-locals 2", WatchUpdate, + postCommand("-stack-list-locals 2", Discardable, CB(handleStackListLocalsClassic)); // stage 2/2 } @@ -754,9 +753,9 @@ void GdbEngine::runDirectDebuggingHelperClassic(const WatchData &data, bool dump QVariant var; var.setValue(data); - postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3Classic), var); + postCommand(cmd, Discardable, CB(handleDebuggingHelperValue3Classic), var); - showStatusMessage(msgRetrievingWatchData(m_pendingWatchRequests + 1), 10000); + showStatusMessage(msgRetrievingWatchData(m_uncompleted.size()), 10000); } void GdbEngine::runDebuggingHelperClassic(const WatchData &data0, bool dumpChildren) @@ -811,25 +810,25 @@ void GdbEngine::runDebuggingHelperClassic(const WatchData &data0, bool dumpChild cmd += ',' + ex; cmd += ')'; - postCommand(cmd, WatchUpdate | NonCriticalResponse); + postCommand(cmd, Discardable | NonCriticalResponse); - showStatusMessage(msgRetrievingWatchData(m_pendingWatchRequests + 1), 10000); + showStatusMessage(msgRetrievingWatchData(m_uncompleted.size()), 10000); // retrieve response - postCommand("p (char*)&qDumpOutBuffer", WatchUpdate, + postCommand("p (char*)&qDumpOutBuffer", Discardable, CB(handleDebuggingHelperValue2Classic), qVariantFromValue(data)); } void GdbEngine::createGdbVariableClassic(const WatchData &data) { PRECONDITION; - postCommand("-var-delete \"" + data.iname + '"', WatchUpdate); + postCommand("-var-delete \"" + data.iname + '"', Discardable); QByteArray exp = data.exp; if (exp.isEmpty() && data.address) exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.hexAddress(); QVariant val = QVariant::fromValue<WatchData>(data); postCommand("-var-create \"" + data.iname + "\" * \"" + exp + '"', - WatchUpdate, CB(handleVarCreate), val); + Discardable, CB(handleVarCreate), val); } void GdbEngine::updateSubItemClassic(const WatchData &data0) @@ -929,7 +928,7 @@ void GdbEngine::updateSubItemClassic(const WatchData &data0) if (debugSubItem) qDebug() << "UPDATE SUBITEM: VALUE"; QByteArray cmd = "-var-evaluate-expression \"" + data.iname + '"'; - postCommand(cmd, WatchUpdate, + postCommand(cmd, Discardable, CB(handleEvaluateExpressionClassic), QVariant::fromValue(data)); return; } @@ -953,7 +952,7 @@ void GdbEngine::updateSubItemClassic(const WatchData &data0) if (data.isChildrenNeeded()) { QTC_ASSERT(!data.variable.isEmpty(), return); // tested above QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; - postCommand(cmd, WatchUpdate, + postCommand(cmd, Discardable, CB(handleVarListChildrenClassic), QVariant::fromValue(data)); return; } @@ -999,7 +998,7 @@ void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response) if (m_cookieForToken.contains(response.token - 1)) { m_cookieForToken.remove(response.token - 1); showMessage(_("DETECTING LOST COMMAND %1").arg(response.token - 1)); - --m_pendingWatchRequests; + // --m_pendingWatchRequests; data.setError(WatchData::msgNotInScope()); insertData(data); return; @@ -1025,7 +1024,7 @@ void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response) parseWatchData(watchHandler()->expandedINames(), data, contents, &list); //for (int i = 0; i != list.size(); ++i) // qDebug() << "READ: " << list.at(i).toString(); - watchHandler()->insertBulkData(list); + watchHandler()->insertData(list); } void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response) @@ -1082,7 +1081,7 @@ void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response) QByteArray cmd = "qdumpqstring (" + data1.exp + ')'; QVariant var; var.setValue(data1); - postCommand(cmd, WatchUpdate, + postCommand(cmd, Discardable, CB(handleDebuggingHelperValue3Classic), var); } } @@ -1101,6 +1100,9 @@ void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response) void GdbEngine::tryLoadDebuggingHelpersClassic() { + if (m_forceAsyncModel) + return; + PRECONDITION; if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) { // Load at least gdb macro based dumpers. @@ -1171,12 +1173,12 @@ void GdbEngine::updateAllClassic() qDebug() << state()); tryLoadDebuggingHelpersClassic(); reloadModulesInternal(); - postCommand("-stack-list-frames", WatchUpdate, + postCommand("-stack-list-frames", Discardable, CB(handleStackListFrames), QVariant::fromValue<StackCookie>(StackCookie(false, true))); stackHandler()->setCurrentIndex(0); if (supportsThreads()) - postCommand("-thread-list-ids", WatchUpdate, CB(handleThreadListIds), 0); + postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds), 0); reloadRegisters(); updateLocals(); } @@ -1248,11 +1250,10 @@ void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response) frame.function, frame.file, frame.line, &uninitializedVariables); } - QList<WatchData> list; foreach (const GdbMi &item, locals) { const WatchData data = localVariable(item, uninitializedVariables, &seen); if (data.isValid()) - list.push_back(data); + insertData(data); } if (!m_resultVarName.isEmpty()) { @@ -1260,10 +1261,9 @@ void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response) rd.iname = "return.0"; rd.name = QLatin1String("return"); rd.exp = m_resultVarName; - list.append(rd); + insertData(rd); } - watchHandler()->insertBulkData(list); watchHandler()->updateWatchers(); } @@ -1371,7 +1371,7 @@ void GdbEngine::handleVarListChildrenHelperClassic(const GdbMi &item, data.setChildrenUnneeded(); QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; //iname += '.' + exp; - postCommand(cmd, WatchUpdate, + postCommand(cmd, Discardable, CB(handleVarListChildrenClassic), QVariant::fromValue(data)); } else if (!startsWithDigit(QLatin1String(exp)) && item.findChild("numchild").data() == "0") { @@ -1390,7 +1390,7 @@ void GdbEngine::handleVarListChildrenHelperClassic(const GdbMi &item, WatchData data; data.iname = name; QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"'; - postCommand(cmd, WatchUpdate, + postCommand(cmd, Discardable, CB(handleVarListChildrenClassic), QVariant::fromValue(data)); } else if (exp == "staticMetaObject") { // && item.findChild("type").data() == "const QMetaObject") @@ -1464,9 +1464,6 @@ void GdbEngine::handleVarListChildrenClassic(const GdbResponse &response) //qDebug() << "VAR_LIST_CHILDREN: PARENT" << data.toString(); QList<GdbMi> children = response.data.findChild("children").children(); - for (int i = 0; i != children.size(); ++i) - handleVarListChildrenHelperClassic(children.at(i), data, i); - if (children.isEmpty()) { // happens e.g. if no debug information is present or // if the class really has no children @@ -1479,14 +1476,18 @@ void GdbEngine::handleVarListChildrenClassic(const GdbResponse &response) insertData(data1); data.setAllUnneeded(); insertData(data); - } else if (data.variable.endsWith("private") + } else { + if (data.variable.endsWith("private") || data.variable.endsWith("protected") || data.variable.endsWith("public")) { // this skips the spurious "public", "private" etc levels // gdb produces - } else { - data.setChildrenUnneeded(); - insertData(data); + } else { + data.setChildrenUnneeded(); + insertData(data); + } + for (int i = 0; i != children.size(); ++i) + handleVarListChildrenHelperClassic(children.at(i), data, i); } } else { data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data())); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 83ee073d05..1a3e87f409 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -255,7 +255,6 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters, m_oldestAcceptableToken = -1; m_nonDiscardableCount = 0; m_outputCodec = QTextCodec::codecForLocale(); - m_pendingWatchRequests = 0; m_pendingBreakpointRequests = 0; m_commandsDoneCallback = 0; m_stackNeeded = false; @@ -263,6 +262,7 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters, m_disassembleUsesComma = false; m_actingOnExpectedStop = false; m_fullStartDone = false; + m_forceAsyncModel = false; invalidateSourcesList(); @@ -788,7 +788,8 @@ void GdbEngine::readGdbStandardOutput() int newstart = 0; int scan = m_inbuffer.size(); - m_inbuffer.append(gdbProc()->readAllStandardOutput()); + QByteArray out = gdbProc()->readAllStandardOutput(); + m_inbuffer.append(out); // This can trigger when a dialog starts a nested event loop. if (m_busy) @@ -811,7 +812,8 @@ void GdbEngine::readGdbStandardOutput() continue; } m_busy = true; - handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start)); + QByteArray ba = QByteArray::fromRawData(m_inbuffer.constData() + start, end - start); + handleResponse(ba); m_busy = false; } m_inbuffer.clear(); @@ -903,17 +905,13 @@ void GdbEngine::postCommandHelper(const GdbCommand &cmd) return; } - if (cmd.flags & RebuildWatchModel) { - ++m_pendingWatchRequests; - PENDING_DEBUG(" WATCH MODEL:" << cmd.command << "=>" << cmd.callbackName - << "INCREMENTS PENDING TO" << m_pendingWatchRequests); - } else if (cmd.flags & RebuildBreakpointModel) { + if (cmd.flags & RebuildBreakpointModel) { ++m_pendingBreakpointRequests; PENDING_DEBUG(" BRWAKPOINT MODEL:" << cmd.command << "=>" << cmd.callbackName << "INCREMENTS PENDING TO" << m_pendingBreakpointRequests); } else { PENDING_DEBUG(" OTHER (IN):" << cmd.command << "=>" << cmd.callbackName - << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests + << "LEAVES PENDING WATCH AT" << m_uncompleted.size() << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests); } @@ -1055,7 +1053,6 @@ void GdbEngine::commandTimeout() if (mb->exec() == QMessageBox::Ok) { showMessage(_("KILLING DEBUGGER AS REQUESTED BY USER")); // This is an undefined state, so we just pull the emergency brake. - watchHandler()->endCycle(); gdbProc()->kill(); } else { showMessage(_("CONTINUE DEBUGGER AS REQUESTED BY USER")); @@ -1205,25 +1202,17 @@ void GdbEngine::handleResultRecord(GdbResponse *response) else if (cmd.adapterCallback) (m_gdbAdapter->*cmd.adapterCallback)(*response); - if (cmd.flags & RebuildWatchModel) { - --m_pendingWatchRequests; - PENDING_DEBUG(" WATCH" << cmd.command << "=>" << cmd.callbackName - << "DECREMENTS PENDING WATCH TO" << m_pendingWatchRequests); - if (m_pendingWatchRequests <= 0) { - PENDING_DEBUG("\n\n ... AND TRIGGERS WATCH MODEL UPDATE\n"); - rebuildWatchModel(); - } - } else if (cmd.flags & RebuildBreakpointModel) { + if (cmd.flags & RebuildBreakpointModel) { --m_pendingBreakpointRequests; PENDING_DEBUG(" BREAKPOINT" << cmd.command << "=>" << cmd.callbackName - << "DECREMENTS PENDING TO" << m_pendingWatchRequests); + << "DECREMENTS PENDING TO" << m_uncompleted.size()); if (m_pendingBreakpointRequests <= 0) { PENDING_DEBUG("\n\n ... AND TRIGGERS BREAKPOINT MODEL UPDATE\n"); attemptBreakpointSynchronization(); } } else { PENDING_DEBUG(" OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName - << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests + << "LEAVES PENDING WATCH AT" << m_uncompleted.size() << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests); } @@ -3918,10 +3907,10 @@ bool GdbEngine::supportsThreads() const // ////////////////////////////////////////////////////////////////////// -bool GdbEngine::showToolTip() +void GdbEngine::showToolTip() { if (m_toolTipContext.isNull()) - return false; + return; const QString expression = m_toolTipContext->expression; const QByteArray iname = tooltipIName(m_toolTipContext->expression); if (DebuggerToolTipManager::debug()) @@ -3929,15 +3918,15 @@ bool GdbEngine::showToolTip() if (!debuggerCore()->boolSetting(UseToolTipsInMainEditor)) { watchHandler()->removeData(iname); - return true; + return; } if (!watchHandler()->isValidToolTip(iname)) { watchHandler()->removeData(iname); - return true; + return; } DebuggerToolTipWidget *tw = new DebuggerToolTipWidget; - tw->setDebuggerModel(TooltipsWatch); + tw->setDebuggerModel(TooltipType); tw->setExpression(expression); tw->setContext(*m_toolTipContext); tw->acquireEngine(this); @@ -3945,7 +3934,6 @@ bool GdbEngine::showToolTip() m_toolTipContext->editor, tw); // Prevent tooltip from re-occurring (classic GDB, QTCREATORBUG-4711). m_toolTipContext.reset(); - return true; } QString GdbEngine::tooltipExpression() const @@ -4034,7 +4022,6 @@ bool GdbEngine::setToolTipExpression(const QPoint &mousePos, toolTip.exp = exp.toLatin1(); toolTip.name = exp; toolTip.iname = tooltipIName(exp); - watchHandler()->removeData(toolTip.iname); watchHandler()->insertData(toolTip); } return true; @@ -4071,19 +4058,12 @@ bool GdbEngine::hasDebuggingHelperForType(const QByteArray &type) const return m_dumperHelper.type(type) != DumperHelper::UnknownType; } - void GdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags) { if (isSynchronous()) { // This should only be called for fresh expanded items, not for // items that had their children retrieved earlier. //qDebug() << "\nUPDATE WATCH DATA: " << data.toString() << "\n"; -#if 0 - WatchData data1 = data; - data1.setAllUnneeded(); - insertData(data1); - rebuildModel(); -#else if (data.iname.endsWith(".")) return; @@ -4106,53 +4086,26 @@ void GdbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &f // triggered e.g. by manually entered command in the gdb console? //qDebug() << "TRY PARTIAL: " << flags.tryIncremental // << hasPython() - // << (m_pendingWatchRequests == 0) // << (m_pendingBreakpointRequests == 0); UpdateParameters params; params.tooltipOnly = data.iname.startsWith("tooltip"); params.tryPartial = flags.tryIncremental && hasPython() - && m_pendingWatchRequests == 0 && m_pendingBreakpointRequests == 0; params.varList = data.iname; updateLocalsPython(params); -#endif } else { - // Bump requests to avoid model rebuilding during the nested - // updateWatchModel runs. - ++m_pendingWatchRequests; - PENDING_DEBUG("UPDATE WATCH BUMPS PENDING UP TO " << m_pendingWatchRequests); -#if 1 - QMetaObject::invokeMethod(this, "updateWatchDataHelper", - Qt::QueuedConnection, Q_ARG(WatchData, data)); -#else - updateWatchDataHelper(data); -#endif + PENDING_DEBUG("UPDATE WATCH BUMPS PENDING UP TO " << m_uncompleted.size()); + updateSubItemClassic(data); } } -void GdbEngine::updateWatchDataHelper(const WatchData &data) -{ - //m_pendingRequests = 0; - PENDING_DEBUG("UPDATE WATCH DATA"); -# if DEBUG_PENDING - //qDebug() << "##############################################"; - qDebug() << "UPDATE MODEL, FOUND INCOMPLETE:"; - //qDebug() << data.toString(); -# endif - - updateSubItemClassic(data); - //PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL"); - --m_pendingWatchRequests; - PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingWatchRequests); - if (m_pendingWatchRequests <= 0) - rebuildWatchModel(); -} - void GdbEngine::rebuildWatchModel() { + QTC_CHECK(m_completed.isEmpty()); + QTC_CHECK(m_uncompleted.isEmpty()); static int count = 0; ++count; if (!isSynchronous()) @@ -4162,7 +4115,6 @@ void GdbEngine::rebuildWatchModel() showMessage(LogWindow::logTimeStamp(), LogMiscInput); showMessage(_("<Rebuild Watchmodel %1>").arg(count), LogMiscInput); showStatusMessage(tr("Finished retrieving data"), 400); - watchHandler()->endCycle(); showToolTip(); handleAutoTests(); } @@ -4332,15 +4284,23 @@ WatchData GdbEngine::localVariable(const GdbMi &item, return data; } -void GdbEngine::insertData(const WatchData &data0) +void GdbEngine::insertData(const WatchData &data) { - PENDING_DEBUG("INSERT DATA" << data0.toString()); - WatchData data = data0; - if (data.value.startsWith(QLatin1String("mi_cmd_var_create:"))) { - qDebug() << "BOGUS VALUE:" << data.toString(); - return; + PENDING_DEBUG("INSERT DATA" << data.toString()); + if (data.isSomethingNeeded()) { + m_uncompleted.insert(data.iname); + WatchUpdateFlags flags; + flags.tryIncremental = true; + updateWatchData(data, flags); + } else { + m_completed.append(data); + m_uncompleted.remove(data.iname); + if (m_uncompleted.isEmpty()) { + watchHandler()->insertData(m_completed); + m_completed.clear(); + rebuildWatchModel(); + } } - watchHandler()->insertData(data); } void GdbEngine::assignValueInDebugger(const WatchData *data, @@ -4901,12 +4861,6 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &settingsIdHint) postCommand("disassemble 0 0", ConsoleCommand, CB(handleDisassemblerCheck)); - if (sp.breakOnMain) { - QByteArray cmd = "tbreak "; - cmd += sp.toolChainAbi.os() == Abi::WindowsOS ? "qMain" : "main"; - postCommand(cmd); - } - if (attemptQuickStart()) { postCommand("set auto-solib-add off", ConsoleCommand); } else { @@ -4941,6 +4895,9 @@ void GdbEngine::loadInitScript() void GdbEngine::loadPythonDumpers() { + if (m_forceAsyncModel) + return; + const QByteArray dumperSourcePath = Core::ICore::resourcePath().toLocal8Bit() + "/dumper/"; @@ -5091,6 +5048,12 @@ void GdbEngine::handleInferiorPrepared() QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); + if (sp.breakOnMain) { + QByteArray cmd = "tbreak "; + cmd += sp.toolChainAbi.os() == Abi::WindowsOS ? "qMain" : "main"; + postCommand(cmd); + } + // Initial attempt to set breakpoints. if (sp.startMode != AttachCore) { showStatusMessage(tr("Setting breakpoints...")); @@ -5361,6 +5324,9 @@ void GdbEngine::requestDebugInformation(const DebugInfoTask &task) bool GdbEngine::attemptQuickStart() const { + if (m_forceAsyncModel) + return false; + // Don't try if the user does not ask for it. if (!debuggerCore()->boolSetting(AttemptQuickStart)) return false; diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 93760ec19c..772f5a4d3f 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -36,6 +36,7 @@ #include "debuggerengine.h" #include "stackframe.h" +#include "watchhandler.h" #include "watchutils.h" #include <QByteArray> @@ -80,16 +81,6 @@ enum DebuggingHelperState DebuggingHelperUnavailable }; -class UpdateParameters -{ -public: - UpdateParameters() { tryPartial = tooltipOnly = false; } - - bool tryPartial; - bool tooltipOnly; - QByteArray varList; -}; - /* This is only used with Mac gdb since 2.2 * * "Custom dumper" is a library compiled against the current @@ -322,9 +313,6 @@ private: ////////// Gdb Command Management ////////// NeedsStop = 1, // No need to wait for the reply before continuing inferior. Discardable = 2, - // Trigger watch model rebuild when no such commands are pending anymore. - RebuildWatchModel = 4, - WatchUpdate = Discardable | RebuildWatchModel, // We can live without receiving an answer. NonCriticalResponse = 8, // Callback expects GdbResultRunning instead of GdbResultDone. @@ -407,7 +395,6 @@ private: ////////// Gdb Command Management ////////// int m_oldestAcceptableToken; int m_nonDiscardableCount; - int m_pendingWatchRequests; // Watch updating commands in flight int m_pendingBreakpointRequests; // Watch updating commands in flight typedef void (GdbEngine::*CommandsDoneCallback)(); @@ -630,13 +617,11 @@ private: ////////// View & Data Stuff ////////// virtual void watchPoint(const QPoint &); void handleWatchPoint(const GdbResponse &response); - // FIXME: BaseClass. called to improve situation for a watch item void updateSubItemClassic(const WatchData &data); - void virtual updateWatchData(const WatchData &data, const WatchUpdateFlags &flags); - Q_SLOT void updateWatchDataHelper(const WatchData &data); + void updateWatchData(const WatchData &data, const WatchUpdateFlags &flags); void rebuildWatchModel(); - bool showToolTip(); + void showToolTip(); void insertData(const WatchData &data); void sendWatchParameters(const QByteArray ¶ms0); @@ -753,6 +738,11 @@ private: ////////// View & Data Stuff ////////// // debug information. bool attemptQuickStart() const; bool m_fullStartDone; + + // Test + bool m_forceAsyncModel; + QList<WatchData> m_completed; + QSet<QByteArray> m_uncompleted; }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp index f69ee66055..b020cc03b0 100644 --- a/src/plugins/debugger/gdb/pythongdbengine.cpp +++ b/src/plugins/debugger/gdb/pythongdbengine.cpp @@ -54,12 +54,11 @@ namespace Internal { void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) { PRECONDITION; - m_pendingWatchRequests = 0; + //m_pendingWatchRequests = 0; m_pendingBreakpointRequests = 0; m_processedNames.clear(); - WatchHandler *handler = watchHandler(); - handler->beginCycle(!params.tryPartial); + WatchHandler *handler = watchHandler(); QByteArray expanded = "expanded:" + handler->expansionRequests() + ' '; expanded += "typeformats:" + handler->typeFormatRequests() + ' '; expanded += "formats:" + handler->individualFormatRequests(); @@ -117,7 +116,7 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) postCommand("bb options:" + options + " vars:" + params.varList + ' ' + resultVar + expanded + " watchers:" + watchers.toHex(), - WatchUpdate, CB(handleStackFramePython), QVariant(params.tryPartial)); + Discardable, CB(handleStackFramePython), QVariant(params.tryPartial)); } void GdbEngine::handleStackFramePython(const GdbResponse &response) @@ -136,9 +135,11 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) } GdbMi all; all.fromStringMultiple(out); - GdbMi data = all.findChild("data"); + + WatchHandler *handler = watchHandler(); QList<WatchData> list; + foreach (const GdbMi &child, data.children()) { WatchData dummy; dummy.iname = child.findChild("iname").data(); @@ -151,7 +152,7 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) } else { dummy.name = _(child.findChild("name").data()); } - parseWatchData(watchHandler()->expandedINames(), dummy, child, &list); + parseWatchData(handler->expandedINames(), dummy, child, &list); } const GdbMi typeInfo = all.findChild("typeinfo"); if (typeInfo.type() == GdbMi::List) { @@ -169,15 +170,20 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) list[i].size = ti.size; } - watchHandler()->insertBulkData(list); + if (!partial) { + handler->removeChildren("local"); + handler->removeChildren("watch"); + } + + handler->insertData(list); //PENDING_DEBUG("AFTER handleStackFrame()"); // FIXME: This should only be used when updateLocals() was // triggered by expanding an item in the view. - if (m_pendingWatchRequests <= 0) { + //if (m_pendingWatchRequests <= 0) { //PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n"); rebuildWatchModel(); - } + //} if (!partial) emit stackFrameCompleted(); } else { diff --git a/src/plugins/debugger/lldb/ipcenginehost.cpp b/src/plugins/debugger/lldb/ipcenginehost.cpp index 73a01839de..7586920ba4 100644 --- a/src/plugins/debugger/lldb/ipcenginehost.cpp +++ b/src/plugins/debugger/lldb/ipcenginehost.cpp @@ -496,9 +496,7 @@ void IPCEngineHost::rpcCallback(quint64 f, QByteArray payload) WatchHandler *wh = watchHandler(); if (!wh) break; - wh->beginCycle(fullCycle); - wh->insertBulkData(wd); - wh->endCycle(); + wh->insertData(wd); } break; case IPCEngineGuest::NotifyAddBreakpointOk: diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 4c02711097..7683cd8bb3 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -720,14 +720,12 @@ void PdbEngine::updateAll() void PdbEngine::updateLocals() { - WatchHandler *handler = watchHandler(); - handler->beginCycle(true); - QByteArray watchers; //if (!m_toolTipExpression.isEmpty()) // watchers += m_toolTipExpression.toLatin1() // + '#' + tooltipINameForExpression(m_toolTipExpression.toLatin1()); + WatchHandler *handler = watchHandler(); QHash<QByteArray, int> watcherNames = handler->watcherNames(); QHashIterator<QByteArray, int> it(watcherNames); while (it.hasNext()) { @@ -831,8 +829,7 @@ void PdbEngine::handleListLocals(const PdbResponse &response) //qDebug() << "CHILD: " << child.toString(); parseWatchData(handler->expandedINames(), dummy, child, &list); } - handler->insertBulkData(list); - handler->endCycle(); + handler->insertData(list); } bool PdbEngine::hasCapability(unsigned cap) const diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 908770961d..67078e9781 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -1000,7 +1000,6 @@ void QmlEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &) { // qDebug() << "UPDATE WATCH DATA" << data.toString(); - //watchHandler()->rebuildModel(); //showStatusMessage(tr("Stopped."), 5000); if (data.isInspect()) { @@ -1020,7 +1019,7 @@ void QmlEngine::updateWatchData(const WatchData &data, if (!data.isSomethingNeeded()) - watchHandler()->insertData(data); + watchHandler()->insertIncompleteData(data); } void QmlEngine::synchronizeWatchers() diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp index 9cb35e0ffd..d2c2793be2 100644 --- a/src/plugins/debugger/qml/qmlinspectoragent.cpp +++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp @@ -103,15 +103,11 @@ void QmlInspectorAgent::updateWatchData(const WatchData &data) if (debug) qDebug() << __FUNCTION__ << "(" << data.id << ")"; - if (data.id) { + if (data.id && !m_fetchDataIds.contains(data.id)) { // objects + m_fetchDataIds << data.id; ObjectReference ref(data.id); m_fetchCurrentObjectsQueryIds << fetchContextObject(ref); - WatchData d = data; - d.setAllUnneeded(); - m_engine->watchHandler()->beginCycle(InspectWatch, false); - m_engine->watchHandler()->insertData(d); - m_engine->watchHandler()->endCycle(InspectWatch); } } @@ -126,12 +122,9 @@ void QmlInspectorAgent::selectObjectInTree(int debugId) if (m_debugIdToIname.contains(debugId)) { QByteArray iname = m_debugIdToIname.value(debugId); QTC_ASSERT(iname.startsWith("inspect."), qDebug() << iname); - QModelIndex itemIndex = m_engine->watchHandler()->itemIndex(iname); - QTC_ASSERT(itemIndex.isValid(), - qDebug() << "No for " << debugId << ", iname " << iname; return;); if (debug) qDebug() << " selecting" << iname << "in tree"; - m_engine->watchHandler()->setCurrentModelIndex(InspectWatch, itemIndex); + m_engine->watchHandler()->setCurrentItem(iname); m_objectToSelect = 0; } else { // we've to fetch it @@ -370,9 +363,8 @@ void QmlInspectorAgent::updateStatus() && debuggerCore()->boolSetting(ShowQmlObjectTree)) { reloadEngines(); } else { - // clear view - m_engine->watchHandler()->beginCycle(InspectWatch, true); - m_engine->watchHandler()->endCycle(InspectWatch); + // Clear view. + m_engine->watchHandler()->removeChildren("inspect"); } } @@ -588,10 +580,7 @@ void QmlInspectorAgent::objectTreeFetched(const ObjectReference &object) << "entries into watch handler ..."; } - WatchHandler *watchHandler = m_engine->watchHandler(); - watchHandler->beginCycle(InspectWatch, true); - watchHandler->insertBulkData(watchData); - watchHandler->endCycle(InspectWatch); + m_engine->watchHandler()->insertData(watchData); if (debug) qDebug() << "inserting entries took" << t.elapsed() << "ms"; @@ -615,15 +604,16 @@ void QmlInspectorAgent::onCurrentObjectsFetched(const ObjectReference &obj) ObjectReference last = m_fetchCurrentObjects.last(); m_fetchCurrentObjects.clear(); + m_fetchDataIds.clear(); if (m_objectToSelect == last.debugId()) { // select item in view QByteArray iname = m_debugIdToIname.value(last.debugId()); - QModelIndex itemIndex = m_engine->watchHandler()->itemIndex(iname); - QTC_ASSERT(itemIndex.isValid(), return); + WatchHandler *handler = m_engine->watchHandler(); + QTC_ASSERT(handler->hasItem(iname), return); if (debug) qDebug() << " selecting" << iname << "in tree"; - m_engine->watchHandler()->setCurrentModelIndex(InspectWatch, itemIndex); + handler->setCurrentItem(iname); m_objectToSelect = -1; } @@ -780,12 +770,11 @@ void QmlInspectorAgent::addObjectToTree(const ObjectReference &obj, // find parent QTC_ASSERT(m_debugIdToIname.contains(parentId), break); QByteArray iname = m_debugIdToIname.value(parentId); - const WatchData *parent = m_engine->watchHandler()->findItem(iname); + WatchHandler *handler = m_engine->watchHandler(); + const WatchData *parent = handler->findData(iname); if (parent) { QList<WatchData> watches = buildWatchData(obj, *parent); - m_engine->watchHandler()->beginCycle(false); - m_engine->watchHandler()->insertBulkData(watches); - m_engine->watchHandler()->endCycle(); + handler->insertData(watches); break; } } diff --git a/src/plugins/debugger/qml/qmlinspectoragent.h b/src/plugins/debugger/qml/qmlinspectoragent.h index 0d0eafbe62..67bd0b8286 100644 --- a/src/plugins/debugger/qml/qmlinspectoragent.h +++ b/src/plugins/debugger/qml/qmlinspectoragent.h @@ -153,6 +153,7 @@ private: DebugIdHash m_debugIdHash; QList<int> m_objectWatches; + QList<int> m_fetchDataIds; }; } // Internal diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp index 1f97e58a54..8d35db37c6 100644 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp +++ b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp @@ -272,7 +272,7 @@ void QmlV8DebuggerClientPrivate::evaluate(const QString expr, bool global, QScriptValue ctxtList = parser.call(QScriptValue(), QScriptValueList() << _(ARRAY )); while (rowCount) { QModelIndex index = localsModel->index(--rowCount, 0); - const WatchData *data = engine->watchHandler()->watchData(LocalsWatch, index); + const WatchData *data = engine->watchHandler()->watchData(index); QScriptValue ctxt = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); ctxt.setProperty(_(NAME), QScriptValue(data->name)); ctxt.setProperty(_(HANDLE), QScriptValue(int(data->id))); @@ -1173,7 +1173,7 @@ void QmlV8DebuggerClient::expandObject(const QByteArray &iname, quint64 objectId { if (objectId == 0) { //We may have got the global object - const WatchData *watch = d->engine->watchHandler()->findItem(iname); + const WatchData *watch = d->engine->watchHandler()->findData(iname); if (watch->value == QLatin1String("global")) { StackHandler *stackHandler = d->engine->stackHandler(); if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { @@ -1706,9 +1706,7 @@ void QmlV8DebuggerClient::setCurrentFrameDetails(const QVariant &bodyVal, const data.setHasChildren(true); data.id = 0; } - d->engine->watchHandler()->beginCycle(); d->engine->watchHandler()->insertData(data); - d->engine->watchHandler()->endCycle(); } const QVariantList currentFrameScopes = currentFrame.value(_("scopes")).toList(); @@ -1785,11 +1783,8 @@ void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &r if (!handlesToLookup.isEmpty()) d->lookup(handlesToLookup); - if (!locals.isEmpty()) { - d->engine->watchHandler()->beginCycle(false); - d->engine->watchHandler()->insertBulkData(locals); - d->engine->watchHandler()->endCycle(); - } + if (!locals.isEmpty()) + d->engine->watchHandler()->insertData(locals); } void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, const QVariant &bodyVal, @@ -1810,7 +1805,7 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con d->scope(index); //Also update "this" QByteArray iname("local.this"); - const WatchData *parent = d->engine->watchHandler()->findItem(iname); + const WatchData *parent = d->engine->watchHandler()->findData(iname); d->localsAndWatchers.insertMulti(parent->id, iname); d->lookup(QList<int>() << parent->id); @@ -1833,7 +1828,7 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con WatchData data; //Do we have request to evaluate a local? if (exp.startsWith("local.")) { - const WatchData *watch = d->engine->watchHandler()->findItem(exp.toLatin1()); + const WatchData *watch = d->engine->watchHandler()->findData(exp.toLatin1()); watchDataList << createWatchDataList(watch, body.properties, refsVal); } else { QByteArray iname = d->engine->watchHandler()->watcherName(exp.toLatin1()); @@ -1854,9 +1849,7 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con watchDataList << data << createWatchDataList(&data, body.properties, refsVal); } //Insert the newly evaluated expression to the Watchers Window - d->engine->watchHandler()->beginCycle(false); - d->engine->watchHandler()->insertBulkData(watchDataList); - d->engine->watchHandler()->endCycle(); + d->engine->watchHandler()->insertData(watchDataList); } } } @@ -1933,7 +1926,7 @@ void QmlV8DebuggerClient::expandLocalsAndWatchers(const QVariant &bodyVal, const if (prepend.startsWith("local.") || prepend.startsWith("watch.")) { //Data for expanded local/watch //Could be an object or function - const WatchData *parent = d->engine->watchHandler()->findItem(prepend); + const WatchData *parent = d->engine->watchHandler()->findData(prepend); watchDataList << createWatchDataList(parent, bodyObjectData.properties, refsVal); } else { //rest @@ -1952,9 +1945,7 @@ void QmlV8DebuggerClient::expandLocalsAndWatchers(const QVariant &bodyVal, const } } - d->engine->watchHandler()->beginCycle(false); - d->engine->watchHandler()->insertBulkData(watchDataList); - d->engine->watchHandler()->endCycle(); + d->engine->watchHandler()->insertData(watchDataList); } QList<WatchData> QmlV8DebuggerClient::createWatchDataList(const WatchData *parent, diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp index e4fe415cdd..4b60edc7cb 100644 --- a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp +++ b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp @@ -403,6 +403,8 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) QByteArray command; stream >> command; + WatchHandler *watchHandler = d->engine->watchHandler(); + if (command == "STOPPED") { d->engine->inferiorSpontaneousStop(); @@ -432,15 +434,13 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) d->engine->stackHandler()->setFrames(ideStackFrames); - d->engine->watchHandler()->beginCycle(); bool needPing = false; foreach (WatchData data, watches) { - data.iname = d->engine->watchHandler()->watcherName(data.exp); - d->engine->watchHandler()->insertData(data); + data.iname = watchHandler->watcherName(data.exp); + watchHandler->insertIncompleteData(data); - if (d->engine->watchHandler()->expandedINames().contains(data.iname) && - qint64(data.id) != -1) { + if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { needPing = true; expandObject(data.iname,data.id); } @@ -448,20 +448,16 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) foreach (WatchData data, locals) { data.iname = "local." + data.exp; - d->engine->watchHandler()->insertData(data); + watchHandler->insertIncompleteData(data); - if (d->engine->watchHandler()->expandedINames().contains(data.iname) && - qint64(data.id) != -1) { + if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { needPing = true; expandObject(data.iname,data.id); } } - if (needPing) { + if (needPing) sendPing(); - } else { - d->engine->watchHandler()->endCycle(); - } bool becauseOfException; stream >> becauseOfException; @@ -518,12 +514,12 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) + QLatin1String(iname) + QLatin1Char(' ') + data.value); data.iname = iname; if (iname.startsWith("watch.")) { - d->engine->watchHandler()->insertData(data); + watchHandler->insertIncompleteData(data); } else if (iname == "console") { d->engine->showMessage(data.value, QtMessageLogOutput); } else if (iname.startsWith("local.")) { data.name = data.name.left(data.name.indexOf(QLatin1Char(' '))); - d->engine->watchHandler()->insertData(data); + watchHandler->insertIncompleteData(data); } else { qWarning() << "QmlEngine: Unexcpected result: " << iname << data.value; } @@ -538,10 +534,9 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) foreach (WatchData data, result) { data.iname = iname + '.' + data.exp; - d->engine->watchHandler()->insertData(data); + watchHandler->insertIncompleteData(data); - if (d->engine->watchHandler()->expandedINames().contains(data.iname) && - qint64(data.id) != -1) { + if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { needPing = true; expandObject(data.iname, data.id); } @@ -560,14 +555,12 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) d->logReceiveMessage(QString::fromLatin1("%1 %2 (%3 x locals) (%4 x watchdata)").arg( QLatin1String(command), QString::number(frameId), QString::number(locals.size()), QString::number(watches.size()))); - d->engine->watchHandler()->beginCycle(); bool needPing = false; foreach (WatchData data, watches) { - data.iname = d->engine->watchHandler()->watcherName(data.exp); - d->engine->watchHandler()->insertData(data); + data.iname = watchHandler->watcherName(data.exp); + watchHandler->insertIncompleteData(data); - if (d->engine->watchHandler()->expandedINames().contains(data.iname) && - qint64(data.id) != -1) { + if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { needPing = true; expandObject(data.iname, data.id); } @@ -575,26 +568,19 @@ void QScriptDebuggerClient::messageReceived(const QByteArray &data) foreach (WatchData data, locals) { data.iname = "local." + data.exp; - d->engine->watchHandler()->insertData(data); - if (d->engine->watchHandler()->expandedINames().contains(data.iname) && - qint64(data.id) != -1) { + watchHandler->insertIncompleteData(data); + if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { needPing = true; expandObject(data.iname, data.id); } } if (needPing) sendPing(); - else - d->engine->watchHandler()->endCycle(); } else if (command == "PONG") { int ping; stream >> ping; - d->logReceiveMessage(QLatin1String(command) + QLatin1Char(' ') + QString::number(ping)); - - if (ping == d->ping) - d->engine->watchHandler()->endCycle(); } else { qDebug() << Q_FUNC_INFO << "Unknown command: " << command; d->logReceiveMessage(QLatin1String(command) + QLatin1String(" UNKNOWN COMMAND!!")); diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp index 16d758f6c9..efe52b8d41 100644 --- a/src/plugins/debugger/script/scriptengine.cpp +++ b/src/plugins/debugger/script/scriptengine.cpp @@ -651,7 +651,6 @@ bool ScriptEngine::checkForBreakCondition(bool byFunction) void ScriptEngine::updateLocals() { QScriptContext *context = m_scriptEngine->currentContext(); - watchHandler()->beginCycle(); SDEBUG(Q_FUNC_INFO); // @@ -686,9 +685,7 @@ void ScriptEngine::updateLocals() data.iname = "local"; data.name = _(data.iname); - watchHandler()->beginCycle(); updateSubItem(data); - watchHandler()->endCycle(); // FIXME: Use an extra thread. This here is evil. m_stopped = true; showStatusMessage(tr("Stopped."), 5000); @@ -809,9 +806,9 @@ void ScriptEngine::updateSubItem(const WatchData &data0) } SDEBUG(msgDebugInsert(data, children)); - watchHandler()->insertData(data); + watchHandler()->insertIncompleteData(data); if (!children.isEmpty()) - watchHandler()->insertBulkData(children); + watchHandler()->insertData(children); } DebuggerEngine *createScriptEngine(const DebuggerStartParameters &sp) diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp index be6efdcaad..e9d3517626 100644 --- a/src/plugins/debugger/watchdata.cpp +++ b/src/plugins/debugger/watchdata.cpp @@ -143,7 +143,6 @@ WatchData::WatchData() : size(0), bitpos(0), bitsize(0), - generation(-1), hasChildren(false), valueEnabled(true), valueEditable(true), @@ -399,8 +398,6 @@ QString WatchData::toToolTip() const if (size) formatToolTipRow(str, tr("Static Object Size"), tr("%1 bytes").arg(size)); formatToolTipRow(str, tr("Internal ID"), QLatin1String(iname)); - formatToolTipRow(str, tr("Generation"), - QString::number(generation)); str << "</table></body></html>"; return res; } diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h index 44f9f97624..2b63c0b71a 100644 --- a/src/plugins/debugger/watchdata.h +++ b/src/plugins/debugger/watchdata.h @@ -135,7 +135,6 @@ public: uint size; // Size uint bitpos; // Position within bit fields uint bitsize; // Size in case of bit fields - qint32 generation; // When updated? bool hasChildren; bool valueEnabled; // Value will be enabled or not bool valueEditable; // Value will be editable diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index c9ada1d5b3..77bc4f9db5 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -40,10 +40,6 @@ #include "debuggerdialogs.h" #include "watchutils.h" -#if USE_WATCH_MODEL_TEST -#include "modeltest.h" -#endif - #include <utils/qtcassert.h> #include <utils/savedaction.h> @@ -52,6 +48,7 @@ #include <QDebug> #include <QEvent> #include <QFile> +#include <QPointer> #include <QProcess> #include <QTextStream> #include <QtAlgorithms> @@ -62,6 +59,12 @@ #include <ctype.h> #include <utils/qtcassert.h> +//#define USE_WATCH_MODEL_TEST 0 +//#define USE_EXPENSIVE_CHECKS 0 + +#if USE_WATCH_MODEL_TEST +#include "modeltest.h" +#endif namespace Debugger { namespace Internal { @@ -70,7 +73,13 @@ namespace Internal { enum { debugModel = 0 }; #define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0) -#define MODEL_DEBUGX(s) qDebug() << s + +#if USE_EXPENSIVE_CHECKS +#define CHECK(s) s +#else +#define CHECK(s) +#endif + static QHash<QByteArray, int> theWatcherNames; static QHash<QByteArray, int> theTypeFormats; @@ -100,126 +109,222 @@ static QByteArray stripForFormat(const QByteArray &ba) return res; } -void WatchHandler::setUnprintableBase(int base) -{ - theUnprintableBase = base; - emitAllChanged(); -} - -int WatchHandler::unprintableBase() -{ - return theUnprintableBase; -} - //////////////////////////////////////////////////////////////////// // // WatchItem // //////////////////////////////////////////////////////////////////// +// Used to make sure the item cache is notified of construction and +// destruction of items. + +class WatchItem; +WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname); +void itemDestructor(WatchModel *model, WatchItem *item); + class WatchItem : public WatchData { public: - WatchItem() { parent = 0; } - - ~WatchItem() { - if (parent != 0) - parent->children.removeOne(this); - qDeleteAll(children); - } - - WatchItem(const WatchData &data) : WatchData(data) - { parent = 0; } + WatchItem *parent; + QList<WatchItem *> children; - void setData(const WatchData &data) - { static_cast<WatchData &>(*this) = data; } +private: + friend WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname); + friend void itemDestructor(WatchModel *model, WatchItem *item); - WatchItem *parent; - QList<WatchItem *> children; // fetched children + WatchItem() { parent = 0; } + ~WatchItem() {} + WatchItem(const WatchItem &); // Not implemented. }; - /////////////////////////////////////////////////////////////////////// // // WatchModel // /////////////////////////////////////////////////////////////////////// -WatchModel::WatchModel(WatchHandler *handler, WatchType type) - : QAbstractItemModel(handler), m_generationCounter(0), - m_handler(handler), m_type(type) +class WatchModel : public QAbstractItemModel { - m_root = new WatchItem; - m_root->hasChildren = 1; - m_root->state = 0; - m_root->name = WatchHandler::tr("Root"); - m_root->parent = 0; + Q_OBJECT - switch (m_type) { - case ReturnWatch: - m_root->iname = "return"; - m_root->name = WatchHandler::tr("Return Value"); - break; - case LocalsWatch: - m_root->iname = "local"; - m_root->name = WatchHandler::tr("Locals"); - break; - case WatchersWatch: - m_root->iname = "watch"; - m_root->name = WatchHandler::tr("Expressions"); - break; - case TooltipsWatch: - m_root->iname = "tooltip"; - m_root->name = WatchHandler::tr("Tooltip"); - break; - case InspectWatch: - m_root->iname = "inspect"; - m_root->name = WatchHandler::tr("Inspector"); - break; - } +private: + explicit WatchModel(WatchHandler *handler); + ~WatchModel(); + + friend WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname); + friend void itemDestructor(WatchModel *model, WatchItem *item); + +public: + int rowCount(const QModelIndex &idx = QModelIndex()) const; + int columnCount(const QModelIndex &idx) const; + +signals: + void currentIndexRequested(const QModelIndex &idx); + void itemIsExpanded(const QModelIndex &idx); + +private: + QVariant data(const QModelIndex &idx, int role) const; + bool setData(const QModelIndex &idx, const QVariant &value, int role); + QModelIndex index(int, int, const QModelIndex &idx) const; + QModelIndex parent(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); + + void invalidateAll(const QModelIndex &parentIndex = QModelIndex()); + WatchItem *createItem(const QByteArray &iname, const QString &name, WatchItem *parent); + + friend class WatchHandler; + + WatchItem *watchItem(const QModelIndex &) const; + QModelIndex watchIndex(const WatchItem *needle) const; + QModelIndex watchIndexHelper(const WatchItem *needle, + const WatchItem *parentItem, const QModelIndex &parentIndex) const; + + void insertDataItem(const WatchData &data); + Q_SLOT void reinsertAllData(); + void reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data); + void insertBulkData(const QList<WatchData> &data); + QString displayForAutoTest(const QByteArray &iname) const; + void reinitialize(); + void destroyItem(WatchItem *item); // With model notification. + void destroyChildren(WatchItem *item); // With model notification. + void destroyHelper(const QList<WatchItem *> &items); // Without model notification. + void emitDataChanged(int column, + const QModelIndex &parentIndex = QModelIndex()); + + friend QDebug operator<<(QDebug d, const WatchModel &m); + + void dump(); + void dumpHelper(WatchItem *item); + Q_SLOT void emitAllChanged(); + + void showInEditorHelper(QString *contents, WatchItem *item, int level); + void setCurrentItem(const QByteArray &iname); + + QString displayType(const WatchData &typeIn) const; + QString formattedValue(const WatchData &data) const; + QString removeInitialNamespace(QString str) const; + QString removeNamespaces(QString str) const; + void formatRequests(QByteArray *out, const WatchItem *item) const; + DebuggerEngine *engine() const; + QString display(const WatchItem *item, int col) const; + int itemFormat(const WatchData &data) const; + bool contentIsValid() const; + + WatchHandler *m_handler; // Not owned. + + WatchItem *m_root; // Owned. + WatchItem *m_localsRoot; // Not owned. + WatchItem *m_inspectorRoot; // Not owned. + WatchItem *m_watchRoot; // Not owned. + WatchItem *m_returnRoot; // Not owned. + WatchItem *m_tooltipRoot; // Not owned. + + QSet<QByteArray> m_expandedINames; + QSet<QByteArray> m_fetchTriggered; + + QStringList typeFormatList(const WatchData &data) const; + TypeFormats m_reportedTypeFormats; + + // QWidgets and QProcesses taking care of special displays. + typedef QMap<QByteArray, QPointer<QObject> > EditHandlers; + EditHandlers m_editHandlers; + + WatchItem *createItem(const QByteArray &iname); + WatchItem *createItem(const WatchData &data); + void assignData(WatchItem *item, const WatchData &data); + WatchItem *findItem(const QByteArray &iname) const; + friend class WatchItem; + QHash<QByteArray, WatchItem *> m_cache; + + #if USE_EXPENSIVE_CHECKS + QHash<const WatchItem *, QByteArray> m_cache2; + void checkTree(); + void checkItem(const WatchItem *item) const; + void checkTree(WatchItem *item, QSet<QByteArray> *inames); + #endif +}; + +WatchModel::WatchModel(WatchHandler *handler) + : m_handler(handler) +{ + m_root = createItem(QByteArray(), tr("Root"), 0); + // Note: Needs to stay + m_localsRoot = createItem("local", tr("Locals"), m_root); + m_inspectorRoot = createItem("inspect", tr("Inspector"), m_root); + m_watchRoot = createItem("watch", tr("Expressions"), m_root); + m_returnRoot = createItem("return", tr("Return Value"), m_root); + m_tooltipRoot = createItem("tooltip", tr("Tooltip"), m_root); + + connect(debuggerCore()->action(SortStructMembers), SIGNAL(valueChanged(QVariant)), + SLOT(reinsertAllData())); + connect(debuggerCore()->action(ShowStdNamespace), SIGNAL(valueChanged(QVariant)), + SLOT(reinsertAllData())); + connect(debuggerCore()->action(ShowQtNamespace), SIGNAL(valueChanged(QVariant)), + SLOT(reinsertAllData())); } WatchModel::~WatchModel() { - delete m_root; + CHECK(checkItem(m_root)); + destroyChildren(m_root); + itemDestructor(this, m_root); + QTC_CHECK(m_cache.isEmpty()); } -WatchItem *WatchModel::rootItem() const +WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname) { - return m_root; + QTC_CHECK(!model->m_cache.contains(iname)); + WatchItem *item = new WatchItem(); + item->iname = iname; + model->m_cache[iname] = item; + CHECK(model->m_cache2[item] = iname); + CHECK(model->checkItem(item)); + return item; } -void WatchModel::reinitialize() +void itemDestructor(WatchModel *model, WatchItem *item) { - int n = m_root->children.size(); - if (n == 0) - return; - //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname); - QModelIndex index = watchIndex(m_root); - beginRemoveRows(index, 0, n - 1); - qDeleteAll(m_root->children); - m_root->children.clear(); - endRemoveRows(); + QTC_ASSERT(model->m_cache.value(item->iname) == item, return); + CHECK(model->checkItem(item)); + CHECK(model->m_cache2.remove(item)); + model->m_cache.remove(item->iname); + delete item; } -void WatchModel::emitAllChanged() +WatchItem *WatchModel::createItem(const QByteArray &iname, const QString &name, WatchItem *parent) { - emit layoutChanged(); + WatchItem *item = itemConstructor(this, iname); + item->name = name; + item->hasChildren = true; // parent == 0; + item->state = 0; + item->parent = parent; + if (parent) + parent->children.append(item); + return item; } -void WatchModel::beginCycle(bool fullCycle) +void WatchModel::reinitialize() { - if (fullCycle) - m_generationCounter++; - - //emit enableUpdates(false); + CHECK(checkTree()); + //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname); + QTC_CHECK(m_root->children.size() == 5); + destroyChildren(m_localsRoot); + destroyChildren(m_watchRoot); + destroyChildren(m_returnRoot); + destroyChildren(m_tooltipRoot); + destroyChildren(m_inspectorRoot); + QTC_CHECK(m_cache.size() == 6); + CHECK(checkTree()); } -void WatchModel::endCycle() +void WatchModel::emitAllChanged() { - removeOutdated(); - m_fetchTriggered.clear(); - //emit enableUpdates(true); + emit layoutChanged(); } DebuggerEngine *WatchModel::engine() const @@ -237,43 +342,85 @@ void WatchModel::dump() void WatchModel::dumpHelper(WatchItem *item) { qDebug() << "ITEM: " << item->iname - << (item->parent ? item->parent->iname : "<none>") - << item->generation; + << (item->parent ? item->parent->iname : "<none>"); foreach (WatchItem *child, item->children) dumpHelper(child); } -void WatchModel::removeOutdated() +void WatchModel::destroyHelper(const QList<WatchItem *> &items) { - foreach (WatchItem *child, m_root->children) - removeOutdatedHelper(child); -#if DEBUG_MODEL -#if USE_WATCH_MODEL_TEST - (void) new ModelTest(this, this); -#endif -#endif -} - -void WatchModel::removeOutdatedHelper(WatchItem *item) -{ - if (item->generation < m_generationCounter) { - destroyItem(item); - } else { - foreach (WatchItem *child, item->children) - removeOutdatedHelper(child); + for (int i = items.size(); --i >= 0; ) { + WatchItem *item = items.at(i); + destroyHelper(item->children); + itemDestructor(this, item); } } void WatchModel::destroyItem(WatchItem *item) { + const QByteArray iname = item->iname; + CHECK(checkTree()); + QTC_ASSERT(m_cache.contains(iname), return); + + // Deregister from model and parent. + // It's sufficient to do this non-recursively. WatchItem *parent = item->parent; - QModelIndex index = watchIndex(parent); - int n = parent->children.indexOf(item); + QTC_ASSERT(parent, return); + QModelIndex parentIndex = watchIndex(parent); + const int i = parent->children.indexOf(item); //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n); - beginRemoveRows(index, n, n); - parent->children.removeAt(n); + beginRemoveRows(parentIndex, i, i); + parent->children.removeAt(i); endRemoveRows(); - delete item; + + // Destroy contents. + destroyHelper(item->children); + itemDestructor(this, item); + QTC_ASSERT(!m_cache.contains(iname), return); + CHECK(checkTree()); +} + +void WatchModel::destroyChildren(WatchItem *item) +{ + CHECK(checkTree()); + QTC_ASSERT(m_cache.contains(item->iname), return); + if (item->children.isEmpty()) + return; + + QList<WatchItem *> items = item->children; + + // Deregister from model and parent. + // It's sufficient to do this non-recursively. + QModelIndex idx = watchIndex(item); + beginRemoveRows(idx, 0, items.size() - 1); + item->children.clear(); + endRemoveRows(); + + // Destroy contents. + destroyHelper(items); + CHECK(checkTree()); +} + +WatchItem *WatchModel::findItem(const QByteArray &iname) const +{ + return m_cache.value(iname, 0); +} + +WatchItem *WatchModel::createItem(const WatchData &data) +{ + WatchItem *item = itemConstructor(this, data.iname); + static_cast<WatchData &>(*item) = data; + return item; +} + +void WatchModel::assignData(WatchItem *item, const WatchData &data) +{ + CHECK(checkItem(item)); + QTC_ASSERT(data.iname == item->iname, + m_cache.remove(item->iname); + m_cache[data.iname] = item); + static_cast<WatchData &>(*item) = data; + CHECK(checkItem(item)); } void WatchModel::reinsertAllData() @@ -281,26 +428,21 @@ void WatchModel::reinsertAllData() QList<WatchData> list; reinsertAllDataHelper(m_root, &list); reinitialize(); - foreach (WatchItem data, list) { - data.setAllUnneeded(); - insertData(data); - } - layoutChanged(); + insertBulkData(list); } void WatchModel::reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data) { data->append(*item); + data->back().setAllUnneeded(); foreach (WatchItem *child, item->children) reinsertAllDataHelper(child, data); } static QByteArray parentName(const QByteArray &iname) { - int pos = iname.lastIndexOf('.'); - if (pos == -1) - return QByteArray(); - return iname.left(pos); + const int pos = iname.lastIndexOf('.'); + return pos == -1 ? QByteArray() : iname.left(pos); } static QString niceTypeHelper(const QByteArray &typeIn) @@ -576,20 +718,27 @@ static inline QVariant editValue(const WatchData &d) return QVariant(translate(stringValue)); } -bool WatchModel::canFetchMore(const QModelIndex &index) const +bool WatchModel::canFetchMore(const QModelIndex &idx) const { - WatchItem *item = watchItem(index); + if (!idx.isValid()) + return false; + if (!contentIsValid()) + return false; + WatchItem *item = watchItem(idx); QTC_ASSERT(item, return false); - return index.isValid() && contentIsValid() && !m_fetchTriggered.contains(item->iname); + if (!item->iname.contains('.')) + return false; + return !m_fetchTriggered.contains(item->iname); } -void WatchModel::fetchMore(const QModelIndex &index) +void WatchModel::fetchMore(const QModelIndex &idx) { - QTC_ASSERT(index.isValid(), return); - WatchItem *item = watchItem(index); + if (!idx.isValid()) + return; // Triggered by ModelTester. + WatchItem *item = watchItem(idx); QTC_ASSERT(item, return); QTC_ASSERT(!m_fetchTriggered.contains(item->iname), return); - m_handler->m_expandedINames.insert(item->iname); + m_expandedINames.insert(item->iname); m_fetchTriggered.insert(item->iname); if (item->children.isEmpty()) { WatchData data = *item; @@ -634,6 +783,8 @@ QModelIndex WatchModel::parent(const QModelIndex &idx) const int WatchModel::rowCount(const QModelIndex &idx) const { + if (!idx.isValid()) + return m_root->children.size(); if (idx.column() > 0) return 0; return watchItem(idx)->children.size(); @@ -653,12 +804,15 @@ bool WatchModel::hasChildren(const QModelIndex &parent) const WatchItem *WatchModel::watchItem(const QModelIndex &idx) const { - return idx.isValid() + WatchItem *item = idx.isValid() ? static_cast<WatchItem*>(idx.internalPointer()) : m_root; + CHECK(checkItem(item)); + return item; } QModelIndex WatchModel::watchIndex(const WatchItem *item) const { + CHECK(checkItem(item)); return watchIndexHelper(item, m_root, QModelIndex()); } @@ -719,13 +873,40 @@ int WatchModel::itemFormat(const WatchData &data) const bool WatchModel::contentIsValid() const { + // FIXME: // inspector doesn't follow normal beginCycle()/endCycle() - if (m_type == InspectWatch) - return true; + //if (m_type == InspectWatch) + // return true; return m_handler->m_contentsValid; } -static inline QString expression(const WatchItem *item) +#if USE_EXPENSIVE_CHECKS +void WatchModel::checkTree() +{ + QSet<QByteArray> inames; + checkTree(m_root, &inames); + QSet<QByteArray> current = m_cache.keys().toSet(); + Q_ASSERT(inames == current); +} + +void WatchModel::checkTree(WatchItem *item, QSet<QByteArray> *inames) +{ + checkItem(item); + inames->insert(item->iname); + for (int i = 0, n = item->children.size(); i != n; ++i) + checkTree(item->children.at(i), inames); +} + +void WatchModel::checkItem(const WatchItem *item) const +{ + Q_ASSERT(item->children.size() < 1000 * 1000); + Q_ASSERT(m_cache2.contains(item)); + Q_ASSERT(m_cache2.value(item) == item->iname); + Q_ASSERT(m_cache.value(item->iname) == item); +} +#endif + +static QString expression(const WatchItem *item) { if (!item->exp.isEmpty()) return QString::fromLatin1(item->exp); @@ -746,11 +927,11 @@ QString WatchModel::display(const WatchItem *item, int col) const QString result; switch (col) { case 0: - if (m_type == WatchersWatch && item->name.isEmpty()) + if (item->parent == m_watchRoot && item->name.isEmpty()) result = tr("<Edit>"); - else if (m_type == ReturnWatch && item->iname.count('.') == 1) + else if (item->parent == m_returnRoot) result = tr("returned value"); - else if (item->name == QLatin1String("*") && item->parent) + else if (item->name == QLatin1String("*")) result = QLatin1Char('*') + item->parent->name; else result = removeInitialNamespace(item->name); @@ -774,7 +955,7 @@ QString WatchModel::display(const WatchItem *item, int col) const QString WatchModel::displayForAutoTest(const QByteArray &iname) const { - const WatchItem *item = findItem(iname, m_root); + WatchItem *item = findItem(iname); if (item) return display(item, 1) + QLatin1Char(' ') + display(item, 2); return QString(); @@ -782,6 +963,9 @@ QString WatchModel::displayForAutoTest(const QByteArray &iname) const QVariant WatchModel::data(const QModelIndex &idx, int role) const { + if (!idx.isValid()) + return QVariant(); // Triggered by ModelTester. + const WatchItem *item = watchItem(idx); const WatchItem &data = *item; @@ -840,10 +1024,10 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const return data.iname; case LocalsExpandedRole: - return m_handler->m_expandedINames.contains(data.iname); + return m_expandedINames.contains(data.iname); case LocalsTypeFormatListRole: - return m_handler->typeFormatList(data); + return typeFormatList(data); case LocalsTypeRole: return removeNamespaces(displayType(data)); @@ -890,13 +1074,16 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const return QVariant(); } -bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role) { - WatchItem &data = *watchItem(index); + if (!idx.isValid()) + return false; // Triggered by ModelTester. + + WatchItem &data = *watchItem(idx); switch (role) { case Qt::EditRole: - switch (index.column()) { + switch (idx.column()) { case 0: // Watch expression: See delegate. break; case 1: // Change value @@ -909,10 +1096,10 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro case LocalsExpandedRole: if (value.toBool()) { // Should already have been triggered by fetchMore() - //QTC_CHECK(m_handler->m_expandedINames.contains(data.iname)); - m_handler->m_expandedINames.insert(data.iname); + //QTC_CHECK(m_expandedINames.contains(data.iname)); + m_expandedINames.insert(data.iname); } else { - m_handler->m_expandedINames.remove(data.iname); + m_expandedINames.remove(data.iname); } break; @@ -933,7 +1120,7 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro } } - emit dataChanged(index, index); + //emit dataChanged(idx, idx); return true; } @@ -990,7 +1177,7 @@ QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int ro return QVariant(); } -QStringList WatchHandler::typeFormatList(const WatchData &data) const +QStringList WatchModel::typeFormatList(const WatchData &data) const { if (data.referencingAddress || isPointerType(data.type)) return QStringList() @@ -1026,7 +1213,7 @@ QStringList WatchHandler::typeFormatList(const WatchData &data) const } // Determine sort order of watch items by sort order or alphabetical inames -// according to setting 'SortStructMembers'. We need a map key for bulkInsert +// according to setting 'SortStructMembers'. We need a map key for insertBulkData // and a predicate for finding the insertion position of a single item. // Set this before using any of the below according to action @@ -1086,98 +1273,153 @@ static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *i return it - list.begin(); } -void WatchModel::insertData(const WatchData &data) +void WatchModel::insertDataItem(const WatchData &data) { +#if USE_WATCH_MODEL_TEST + (void) new ModelTest(this, this); +#endif + m_fetchTriggered.remove(data.iname); + CHECK(checkTree()); + QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return); - WatchItem *parent = findItem(parentName(data.iname), m_root); + WatchItem *parent = findItem(parentName(data.iname)); if (!parent) { WatchData parent; parent.iname = parentName(data.iname); MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname); if (!parent.iname.isEmpty()) - insertData(parent); + insertDataItem(parent); return; } - QModelIndex index = watchIndex(parent); - if (WatchItem *oldItem = findItem(data.iname, parent)) { - bool hadChildren = oldItem->hasChildren; + + WatchItem *item = findItem(data.iname); +#if 0 + if (item) { // Overwrite old entry. - bool hasChanged = oldItem->hasChanged(data); - oldItem->setData(data); - oldItem->changed = hasChanged; - oldItem->generation = m_generationCounter; - QModelIndex idx = watchIndex(oldItem); - emit dataChanged(idx, idx.sibling(idx.row(), 2)); + bool hasChanged = item->hasChanged(data); + assignData(item, data); + item->changed = hasChanged; + // QModelIndex idx = watchIndex(oldItem); + // emit dataChanged(idx, idx.sibling(idx.row(), 2)); + } else { + // Add new entry. + item = createItem(data); + item->parent = parent; + item->changed = true; + } + const int n = findInsertPosition(parent->children, item); + QModelIndex idx = watchIndex(parent); + beginInsertRows(idx, n, n); + parent->children.insert(n, item); + endInsertRows(); +#else + if (item) { + // Remove old children. + destroyChildren(item); - // This works around https://bugreports.qt-project.org/browse/QTBUG-7115 - // by creating and destroying a dummy child item. - if (!hadChildren && oldItem->hasChildren) { - WatchData dummy = data; - dummy.iname = data.iname + ".x"; - dummy.hasChildren = false; - dummy.setAllUnneeded(); - insertData(dummy); - destroyItem(findItem(dummy.iname, m_root)); - } + // Overwrite old entry. + bool hasChanged = item->hasChanged(data); + assignData(item, data); + item->changed = hasChanged; + QModelIndex idx = watchIndex(item); + emit dataChanged(idx, idx.sibling(idx.row(), 2)); } else { // Add new entry. - WatchItem *item = new WatchItem(data); + item = createItem(data); item->parent = parent; - item->generation = m_generationCounter; item->changed = true; - const int n = findInsertPosition(parent->children, item); - beginInsertRows(index, n, n); - parent->children.insert(n, item); + const int row = findInsertPosition(parent->children, item); + QModelIndex idx = watchIndex(parent); + beginInsertRows(idx, row, row); + parent->children.insert(row, item); endInsertRows(); + if (m_expandedINames.contains(parentName(data.iname))) { + emit itemIsExpanded(idx); + } +// if (m_expandedINames.contains(data.iname)) { +// QModelIndex child = index(row, 0, idx); +// emit itemIsExpanded(child); +// } } +#endif } void WatchModel::insertBulkData(const QList<WatchData> &list) { + foreach (const WatchData &data, list) + insertDataItem(data); + CHECK(checkTree()); + return; + #if 0 - for (int i = 0; i != list.size(); ++i) - insertData(list.at(i)); + QMap<QByteArray, QList<WatchData> > hash; + + foreach (const WatchData &data, list) { + // we insert everything, including incomplete stuff + // to reduce the number of row add operations in the model. + if (data.isValid()) { + hash[parentName(data.iname)].append(data); + } else { + qWarning("%s:%d: Attempt to bulk-insert invalid watch item: %s", + __FILE__, __LINE__, qPrintable(data.toString())); + } + } + foreach (const QByteArray &parentIName, hash.keys()) { + // FIXME + insertBulkDataX(hash[parentIName]); + } + + foreach (const WatchData &data, list) { + if (data.isSomethingNeeded()) + m_engine->updateWatchData(data); + } + const int n = list.size(); +#if 0 + for (int i = 0; i != n; ++i) + insertData(list.at(i), false); + layoutChanged(); return; #endif // This method does not properly insert items in proper "iname sort - // order", leading to random removal of items in removeOutdated(); + // order". //qDebug() << "WMI:" << list.toString(); //static int bulk = 0; //foreach (const WatchItem &data, list) // qDebug() << "BULK: " << ++bulk << data.toString(); - QTC_ASSERT(!list.isEmpty(), return); + QTC_ASSERT(n > 0, return); QByteArray parentIName = parentName(list.at(0).iname); - WatchItem *parent = findItem(parentIName, m_root); + WatchItem *parent = m_handler->findItem(parentIName); if (!parent) { WatchData parent; parent.iname = parentIName; - insertData(parent); + insertDataItem(parent, true); MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << list.at(0).iname); return; } - QModelIndex index = watchIndex(parent); + QModelIndex idx = watchIndex(parent); sortWatchDataAlphabetically = debuggerCore()->boolSetting(SortStructMembers); QMap<WatchDataSortKey, WatchData> newList; typedef QMap<WatchDataSortKey, WatchData>::iterator Iterator; - foreach (const WatchItem &data, list) - newList.insert(WatchDataSortKey(data), data); - if (newList.size() != list.size()) { - qDebug() << "LIST: "; - foreach (const WatchItem &data, list) - qDebug() << data.toString(); - qDebug() << "NEW LIST: "; - foreach (const WatchItem &data, newList) - qDebug() << data.toString(); - qDebug() << "P->CHILDREN: "; - foreach (const WatchItem *item, parent->children) - qDebug() << item->toString(); - qDebug() - << "P->CHILDREN.SIZE: " << parent->children.size() - << "NEWLIST SIZE: " << newList.size() - << "LIST SIZE: " << list.size(); - } + for (int i = 0; i != n; ++i) + newList.insert(WatchDataSortKey(list.at(i)), list.at(i)); + + //if (newList.size() != n) { + // qDebug() << "LIST: "; + // for (int i = 0; i != n; ++i) + // qDebug() << list.at(i).toString(); + // qDebug() << "NEW LIST: "; + // foreach (const WatchData &data, newList) + // qDebug() << data.toString(); + // qDebug() << "P->CHILDREN: "; + // foreach (const WatchItem *item, parent->children) + // qDebug() << item->toString(); + // qDebug() + // << "P->CHILDREN.SIZE: " << parent->children.size() + // << "NEWLIST SIZE: " << newList.size() + // << "LIST SIZE: " << list.size(); + //} QTC_ASSERT(newList.size() == list.size(), return); foreach (WatchItem *oldItem, parent->children) { @@ -1185,12 +1427,9 @@ void WatchModel::insertBulkData(const QList<WatchData> &list) Iterator it = newList.find(oldSortKey); if (it == newList.end()) { WatchData data = *oldItem; - data.generation = m_generationCounter; newList.insert(oldSortKey, data); } else { it->changed = it->hasChanged(*oldItem); - if (it->generation == -1) - it->generation = m_generationCounter; } } @@ -1206,9 +1445,7 @@ void WatchModel::insertBulkData(const QList<WatchData> &list) if (!parent->children[i]->isEqual(*it)) { qDebug() << "REPLACING" << parent->children.at(i)->iname << " WITH " << it->iname << it->generation; - parent->children[i]->setData(*it); - if (parent->children[i]->generation == -1) - parent->children[i]->generation = m_generationCounter; + m_handler->setData(parent->children[i], *it); //emit dataChanged(idx.sibling(i, 0), idx.sibling(i, 2)); } else { //qDebug() << "SKIPPING REPLACEMENT" << parent->children.at(i)->iname; @@ -1218,14 +1455,12 @@ void WatchModel::insertBulkData(const QList<WatchData> &list) // add new items if (oldCount < newList.size()) { - beginInsertRows(index, oldCount, newList.size() - 1); + beginInsertRows(idx, oldCount, newList.size() - 1); //MODEL_DEBUG("INSERT : " << data.iname << data.value); for (int i = oldCount; i < newList.size(); ++i, ++it) { - WatchItem *item = new WatchItem(*it); + WatchItem *item = m_handler->createItem(*it); qDebug() << "ADDING" << it->iname; item->parent = parent; - if (item->generation == -1) - item->generation = m_generationCounter; item->changed = true; parent->children.append(item); } @@ -1233,16 +1468,7 @@ void WatchModel::insertBulkData(const QList<WatchData> &list) } //qDebug() << "ITEMS: " << parent->children.size(); dump(); -} - -WatchItem *WatchModel::findItem(const QByteArray &iname, WatchItem *root) const -{ - if (root->iname == iname) - return root; - for (int i = root->children.size(); --i >= 0; ) - if (WatchItem *item = findItem(iname, root->children.at(i))) - return item; - return 0; +#endif } static void debugRecursion(QDebug &d, const WatchItem *item, int depth) @@ -1271,6 +1497,29 @@ void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const formatRequests(out, child); } +void WatchModel::showInEditorHelper(QString *contents, WatchItem *item, int depth) +{ + const QChar tab = QLatin1Char('\t'); + const QChar nl = QLatin1Char('\n'); + contents->append(QString(depth, tab)); + contents->append(item->name); + contents->append(tab); + contents->append(item->value); + contents->append(tab); + contents->append(item->type); + contents->append(nl); + foreach (WatchItem *child, item->children) + showInEditorHelper(contents, child, depth + 1); +} + +void WatchModel::setCurrentItem(const QByteArray &iname) +{ + if (WatchItem *item = findItem(iname)) { + QModelIndex idx = watchIndex(item); + emit currentIndexRequested(idx); + } +} + /////////////////////////////////////////////////////////////////////// // // WatchHandler @@ -1280,92 +1529,39 @@ void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const WatchHandler::WatchHandler(DebuggerEngine *engine) { m_engine = engine; - m_inChange = false; m_watcherCounter = debuggerCore()->sessionValue(QLatin1String("Watchers")) .toStringList().count(); - - m_return = new WatchModel(this, ReturnWatch); - m_locals = new WatchModel(this, LocalsWatch); - m_watchers = new WatchModel(this, WatchersWatch); - m_tooltips = new WatchModel(this, TooltipsWatch); - m_inspect = new WatchModel(this, InspectWatch); - + m_model = new WatchModel(this); m_contentsValid = false; + m_contentsValid = true; // FIXME m_resetLocationScheduled = false; - - connect(debuggerCore()->action(SortStructMembers), SIGNAL(valueChanged(QVariant)), - SLOT(reinsertAllData())); - connect(debuggerCore()->action(ShowStdNamespace), SIGNAL(valueChanged(QVariant)), - SLOT(reinsertAllData())); - connect(debuggerCore()->action(ShowQtNamespace), SIGNAL(valueChanged(QVariant)), - SLOT(reinsertAllData())); -} - -void WatchHandler::beginCycle(bool fullCycle) -{ - m_return->beginCycle(fullCycle); - m_locals->beginCycle(fullCycle); - m_watchers->beginCycle(fullCycle); - m_tooltips->beginCycle(fullCycle); - // don't sync m_inspect here: It's updated on it's own -} - -void WatchHandler::endCycle() -{ - m_return->endCycle(); - m_locals->endCycle(); - m_watchers->endCycle(); - m_tooltips->endCycle(); - - m_contentsValid = true; - m_resetLocationScheduled = false; - - updateWatchersWindow(); } -void WatchHandler::beginCycle(WatchType type, bool fullCycle) +WatchHandler::~WatchHandler() { - model(type)->beginCycle(fullCycle); -} - -void WatchHandler::endCycle(WatchType type) -{ - model(type)->endCycle(); + // Do it manually to prevent calling back in model destructors + // after m_cache is destroyed. + delete m_model; + m_model = 0; } void WatchHandler::cleanup() { - m_expandedINames.clear(); + m_model->m_expandedINames.clear(); theWatcherNames.remove(QByteArray()); - m_return->reinitialize(); - m_locals->reinitialize(); - m_tooltips->reinitialize(); - m_inspect->reinitialize(); - m_return->m_fetchTriggered.clear(); - m_locals->m_fetchTriggered.clear(); - m_watchers->m_fetchTriggered.clear(); - m_tooltips->m_fetchTriggered.clear(); - m_inspect->m_fetchTriggered.clear(); + m_model->reinitialize(); + m_model->m_fetchTriggered.clear(); #if 1 - for (EditHandlers::ConstIterator it = m_editHandlers.begin(); - it != m_editHandlers.end(); ++it) { + for (WatchModel::EditHandlers::ConstIterator it = m_model->m_editHandlers.begin(); + it != m_model->m_editHandlers.end(); ++it) { if (!it.value().isNull()) delete it.value(); } - m_editHandlers.clear(); + m_model->m_editHandlers.clear(); #endif } -void WatchHandler::emitAllChanged() -{ - m_return->emitAllChanged(); - m_locals->emitAllChanged(); - m_watchers->emitAllChanged(); - m_tooltips->emitAllChanged(); - m_inspect->emitAllChanged(); -} - -void WatchHandler::insertData(const WatchData &data) +void WatchHandler::insertIncompleteData(const WatchData &data) { MODEL_DEBUG("INSERTDATA: " << data.toString()); if (!data.isValid()) { @@ -1374,14 +1570,10 @@ void WatchHandler::insertData(const WatchData &data) return; } - if (data.isSomethingNeeded() && data.iname.contains(".")) { + if (data.isSomethingNeeded() && data.iname.contains('.')) { MODEL_DEBUG("SOMETHING NEEDED: " << data.toString()); - if (!m_engine->isSynchronous() - || data.isInspect()) { - WatchModel *model = modelForIName(data.iname); - QTC_ASSERT(model, return); - model->insertData(data); - + if (!m_engine->isSynchronous() || data.isInspect()) { + m_model->insertDataItem(data); m_engine->updateWatchData(data); } else { m_engine->showMessage(QLatin1String("ENDLESS LOOP: SOMETHING NEEDED: ") @@ -1390,71 +1582,47 @@ void WatchHandler::insertData(const WatchData &data) data1.setAllUnneeded(); data1.setValue(QLatin1String("<unavailable synchronous data>")); data1.setHasChildren(false); - WatchModel *model = modelForIName(data.iname); - QTC_ASSERT(model, return); - model->insertData(data1); + m_model->insertDataItem(data1); } } else { - WatchModel *model = modelForIName(data.iname); - QTC_ASSERT(model, return); MODEL_DEBUG("NOTHING NEEDED: " << data.toString()); - model->insertData(data); + m_model->insertDataItem(data); showEditValue(data); } } -void WatchHandler::reinsertAllData() +void WatchHandler::insertData(const WatchData &data) { - m_locals->reinsertAllData(); - m_watchers->reinsertAllData(); - m_tooltips->reinsertAllData(); - m_return->reinsertAllData(); - m_inspect->reinsertAllData(); + QList<WatchData> list; + list.append(data); + insertData(list); } -// Bulk-insertion -void WatchHandler::insertBulkData(const QList<WatchData> &list) +void WatchHandler::insertData(const QList<WatchData> &list) { -#if 1 - foreach (const WatchItem &data, list) - insertData(data); - return; -#endif - - if (list.isEmpty()) - return; - QMap<QByteArray, QList<WatchData> > hash; + m_model->insertBulkData(list); - foreach (const WatchData &data, list) { - // we insert everything, including incomplete stuff - // to reduce the number of row add operations in the model. - if (data.isValid()) { - hash[parentName(data.iname)].append(data); - } else { - qWarning("%s:%d: Attempt to bulk-insert invalid watch item: %s", - __FILE__, __LINE__, qPrintable(data.toString())); - } - } - foreach (const QByteArray &parentIName, hash.keys()) { - WatchModel *model = modelForIName(parentIName); - QTC_ASSERT(model, return); - model->insertBulkData(hash[parentIName]); - } + m_contentsValid = true; + updateWatchersWindow(); +} - foreach (const WatchData &data, list) { - if (data.isSomethingNeeded()) - m_engine->updateWatchData(data); - } +void WatchHandler::removeAllData() +{ + m_model->reinitialize(); } void WatchHandler::removeData(const QByteArray &iname) { - WatchModel *model = modelForIName(iname); - if (!model) - return; - WatchItem *item = model->findItem(iname, model->m_root); + WatchItem *item = m_model->findItem(iname); + if (item) + m_model->destroyItem(item); +} + +void WatchHandler::removeChildren(const QByteArray &iname) +{ + WatchItem *item = m_model->findItem(iname); if (item) - model->destroyItem(item); + m_model->destroyChildren(item); } QByteArray WatchHandler::watcherName(const QByteArray &exp) @@ -1483,14 +1651,13 @@ void WatchHandler::watchExpression(const QString &exp) data.setAllUnneeded(); data.setValue(QString(QLatin1Char(' '))); data.setHasChildren(false); - insertData(data); + insertIncompleteData(data); } else if (m_engine->isSynchronous()) { m_engine->updateWatchData(data); } else { - insertData(data); + insertIncompleteData(data); } updateWatchersWindow(); - emitAllChanged(); } static void swapEndian(char *d, int nchar) @@ -1509,9 +1676,9 @@ static void swapEndian(char *d, int nchar) void WatchHandler::showEditValue(const WatchData &data) { const QByteArray key = data.address ? data.hexAddress() : data.iname; - QObject *w = m_editHandlers.value(key); + QObject *w = m_model->m_editHandlers.value(key); if (data.editformat == 0x0) { - m_editHandlers.remove(data.iname); + m_model->m_editHandlers.remove(data.iname); delete w; } else if (data.editformat == 1 || data.editformat == 3) { // QImage @@ -1524,7 +1691,7 @@ void WatchHandler::showEditValue(const WatchData &data) QLatin1String(data.hexAddress())) : tr("%1 Object at Unknown Address").arg(QLatin1String(data.type)); l->setWindowTitle(title); - m_editHandlers[key] = l; + m_model->m_editHandlers[key] = l; } int width, height, format; QByteArray ba; @@ -1563,7 +1730,7 @@ void WatchHandler::showEditValue(const WatchData &data) if (!t) { delete w; t = new QTextEdit; - m_editHandlers[key] = t; + m_model->m_editHandlers[key] = t; } QByteArray ba = QByteArray::fromHex(data.editvalue); QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2); @@ -1580,7 +1747,7 @@ void WatchHandler::showEditValue(const WatchData &data) p = new QProcess; p->start(QLatin1String(cmd)); p->waitForStarted(); - m_editHandlers[key] = p; + m_model->m_editHandlers[key] = p; } p->write(input + '\n'); } else { @@ -1592,13 +1759,10 @@ void WatchHandler::clearWatches() { if (theWatcherNames.isEmpty()) return; - const QList<WatchItem *> watches = m_watchers->rootItem()->children; - for (int i = watches.size() - 1; i >= 0; i--) - m_watchers->destroyItem(watches.at(i)); + m_model->destroyChildren(m_model->m_watchRoot); theWatcherNames.clear(); m_watcherCounter = 0; updateWatchersWindow(); - emitAllChanged(); saveWatchers(); } @@ -1607,12 +1771,12 @@ void WatchHandler::removeWatchExpression(const QString &exp0) QByteArray exp = exp0.toLatin1(); MODEL_DEBUG("REMOVE WATCH: " << exp); theWatcherNames.remove(exp); - foreach (WatchItem *item, m_watchers->rootItem()->children) { + + foreach (WatchItem *item, m_model->m_watchRoot->children) { if (item->exp == exp) { - m_watchers->destroyItem(item); + m_model->destroyItem(item); saveWatchers(); updateWatchersWindow(); - emitAllChanged(); break; } } @@ -1621,7 +1785,15 @@ void WatchHandler::removeWatchExpression(const QString &exp0) void WatchHandler::updateWatchersWindow() { // Force show/hide of watchers and return view. - debuggerCore()->updateWatchersWindow(); + static int previousShowWatch = -1; + static int previousShowReturn = -1; + int showWatch = !m_model->m_watchRoot->children.isEmpty(); + int showReturn = !m_model->m_returnRoot->children.isEmpty(); + if (showWatch == previousShowWatch && showReturn == previousShowReturn) + return; + previousShowWatch = showWatch; + previousShowReturn = showReturn; + debuggerCore()->updateWatchersWindow(showWatch, showReturn); } QStringList WatchHandler::watchedExpressions() @@ -1684,18 +1856,14 @@ void WatchHandler::loadSessionData() theWatcherNames.clear(); m_watcherCounter = 0; QVariant value = debuggerCore()->sessionValue(QLatin1String("Watchers")); - foreach (WatchItem *item, m_watchers->rootItem()->children) - m_watchers->destroyItem(item); + m_model->destroyChildren(m_model->m_watchRoot); foreach (const QString &exp, value.toStringList()) watchExpression(exp); - updateWatchersWindow(); - emitAllChanged(); } void WatchHandler::updateWatchers() { - foreach (WatchItem *item, m_watchers->rootItem()->children) - m_watchers->destroyItem(item); + m_model->destroyChildren(m_model->m_watchRoot); // Copy over all watchers and mark all watchers as incomplete. foreach (const QByteArray &exp, theWatcherNames.keys()) { WatchData data; @@ -1703,67 +1871,33 @@ void WatchHandler::updateWatchers() data.setAllNeeded(); data.name = QLatin1String(exp); data.exp = exp; - insertData(data); + insertIncompleteData(data); } } -WatchModel *WatchHandler::model(WatchType type) const +QAbstractItemModel *WatchHandler::model() const { - switch (type) { - case ReturnWatch: return m_return; - case LocalsWatch: return m_locals; - case WatchersWatch: return m_watchers; - case TooltipsWatch: return m_tooltips; - case InspectWatch: return m_inspect; - } - QTC_CHECK(false); - return 0; + return m_model; } -WatchModel *WatchHandler::modelForIName(const QByteArray &iname) const +const WatchData *WatchHandler::watchData(const QModelIndex &idx) const { - if (iname.startsWith("return")) - return m_return; - if (iname.startsWith("local")) - return m_locals; - if (iname.startsWith("tooltip")) - return m_tooltips; - if (iname.startsWith("watch")) - return m_watchers; - if (iname.startsWith("inspect")) - return m_inspect; - QTC_ASSERT(false, qDebug() << "INAME: " << iname); - return 0; + return m_model->watchItem(idx); } -const WatchData *WatchHandler::watchData(WatchType type, const QModelIndex &index) const +const WatchData *WatchHandler::findData(const QByteArray &iname) const { - if (index.isValid()) - if (const WatchModel *m = model(type)) - return m->watchItem(index); - return 0; -} - -const WatchData *WatchHandler::findItem(const QByteArray &iname) const -{ - const WatchModel *model = modelForIName(iname); - QTC_ASSERT(model, return 0); - return model->findItem(iname, model->m_root); + return m_model->findItem(iname); } QString WatchHandler::displayForAutoTest(const QByteArray &iname) const { - const WatchModel *model = modelForIName(iname); - QTC_ASSERT(model, return QString()); - return model->displayForAutoTest(iname); + return m_model->displayForAutoTest(iname); } -QModelIndex WatchHandler::itemIndex(const QByteArray &iname) const +bool WatchHandler::hasItem(const QByteArray &iname) const { - if (const WatchModel *model = modelForIName(iname)) - if (WatchItem *item = model->findItem(iname, model->m_root)) - return model->watchIndex(item); - return QModelIndex(); + return m_model->findItem(iname); } void WatchHandler::setFormat(const QByteArray &type0, int format) @@ -1774,17 +1908,13 @@ void WatchHandler::setFormat(const QByteArray &type0, int format) else theTypeFormats[type] = format; saveTypeFormats(); - m_return->emitDataChanged(1); - m_locals->emitDataChanged(1); - m_watchers->emitDataChanged(1); - m_tooltips->emitDataChanged(1); - m_inspect->emitDataChanged(1); + m_model->emitDataChanged(1); } int WatchHandler::format(const QByteArray &iname) const { int result = -1; - if (const WatchData *item = findItem(iname)) { + if (const WatchData *item = m_model->findItem(iname)) { int result = theIndividualFormats.value(item->iname, -1); if (result == -1) result = theTypeFormats.value(stripForFormat(item->type), -1); @@ -1795,10 +1925,9 @@ int WatchHandler::format(const QByteArray &iname) const QByteArray WatchHandler::expansionRequests() const { QByteArray ba; - //m_locals->formatRequests(&ba, m_locals->m_root); - //m_watchers->formatRequests(&ba, m_watchers->m_root); - if (!m_expandedINames.isEmpty()) { - QSetIterator<QByteArray> jt(m_expandedINames); + m_model->formatRequests(&ba, m_model->m_root); + if (!m_model->m_expandedINames.isEmpty()) { + QSetIterator<QByteArray> jt(m_model->m_expandedINames); while (jt.hasNext()) { QByteArray iname = jt.next(); ba.append(iname); @@ -1845,67 +1974,43 @@ QByteArray WatchHandler::individualFormatRequests() const void WatchHandler::addTypeFormats(const QByteArray &type, const QStringList &formats) { - m_reportedTypeFormats.insert(QLatin1String(stripForFormat(type)), formats); + m_model->m_reportedTypeFormats.insert(QLatin1String(stripForFormat(type)), formats); } QString WatchHandler::editorContents() { QString contents; - showInEditorHelper(&contents, m_locals->m_root, 0); - showInEditorHelper(&contents, m_watchers->m_root, 0); + m_model->showInEditorHelper(&contents, m_model->m_root, 0); return contents; } -void WatchHandler::showInEditorHelper(QString *contents, WatchItem *item, int depth) -{ - const QChar tab = QLatin1Char('\t'); - const QChar nl = QLatin1Char('\n'); - contents->append(QString(depth, tab)); - contents->append(item->name); - contents->append(tab); - contents->append(item->value); - contents->append(tab); - contents->append(item->type); - contents->append(nl); - foreach (WatchItem *child, item->children) - showInEditorHelper(contents, child, depth + 1); -} - void WatchHandler::removeTooltip() { - m_tooltips->reinitialize(); - m_tooltips->emitAllChanged(); + m_model->destroyChildren(m_model->m_tooltipRoot); } void WatchHandler::rebuildModel() { - beginCycle(); - - const QList<WatchItem *> watches = m_watchers->rootItem()->children; - for (int i = watches.size() - 1; i >= 0; i--) - m_watchers->destroyItem(watches.at(i)); - - foreach (const QString &exp, watchedExpressions()) { - WatchData data; - data.exp = exp.toLatin1(); - data.name = exp; - data.iname = watcherName(data.exp); - data.setAllUnneeded(); - - insertData(data); - } +// m_model->destroyChildren(m_model->m_watchRoot); - endCycle(); +// foreach (const QString &exp, watchedExpressions()) { +// WatchData data; +// data.exp = exp.toLatin1(); +// data.name = exp; +// data.iname = watcherName(data.exp); +// data.setAllUnneeded(); +// insertIncompleteData(data); +// } } void WatchHandler::setTypeFormats(const TypeFormats &typeFormats) { - m_reportedTypeFormats = typeFormats; + m_model->m_reportedTypeFormats = typeFormats; } TypeFormats WatchHandler::typeFormats() const { - return m_reportedTypeFormats; + return m_model->m_reportedTypeFormats; } void WatchHandler::editTypeFormats(bool includeLocals, const QByteArray &iname) @@ -1914,11 +2019,11 @@ void WatchHandler::editTypeFormats(bool includeLocals, const QByteArray &iname) TypeFormatsDialog dlg(0); //QHashIterator<QString, QStringList> it(m_reportedTypeFormats); - QList<QString> l = m_reportedTypeFormats.keys(); + QList<QString> l = m_model->m_reportedTypeFormats.keys(); qSort(l.begin(), l.end()); foreach (const QString &ba, l) { int f = iname.isEmpty() ? -1 : format(iname); - dlg.addTypeFormats(ba, m_reportedTypeFormats.value(ba), f); + dlg.addTypeFormats(ba, m_model->m_reportedTypeFormats.value(ba), f); } if (dlg.exec()) setTypeFormats(dlg.typeFormats()); @@ -1927,6 +2032,7 @@ void WatchHandler::editTypeFormats(bool includeLocals, const QByteArray &iname) void WatchHandler::scheduleResetLocation() { m_contentsValid = false; + //m_contentsValid = true; // FIXME m_resetLocationScheduled = true; } @@ -1934,26 +2040,19 @@ void WatchHandler::resetLocation() { if (m_resetLocationScheduled) { m_resetLocationScheduled = false; - m_return->invalidateAll(); - m_locals->invalidateAll(); - m_watchers->invalidateAll(); - m_tooltips->invalidateAll(); - m_inspect->invalidateAll(); + //m_model->invalidateAll(); FIXME } } bool WatchHandler::isValidToolTip(const QByteArray &iname) const { - WatchItem *item = m_tooltips->findItem(iname, m_tooltips->m_root); + WatchItem *item = m_model->findItem(iname); return item && !item->type.trimmed().isEmpty(); } -void WatchHandler::setCurrentModelIndex(WatchType modelType, - const QModelIndex &index) +void WatchHandler::setCurrentItem(const QByteArray &iname) { - if (WatchModel *m = model(modelType)) { - emit m->setCurrentIndex(index); - } + m_model->setCurrentItem(iname); } QHash<QByteArray, int> WatchHandler::watcherNames() @@ -1961,5 +2060,28 @@ QHash<QByteArray, int> WatchHandler::watcherNames() return theWatcherNames; } +void WatchHandler::setUnprintableBase(int base) +{ + theUnprintableBase = base; + m_model->emitAllChanged(); +} + +int WatchHandler::unprintableBase() +{ + return theUnprintableBase; +} + +bool WatchHandler::isExpandedIName(const QByteArray &iname) const +{ + return m_model->m_expandedINames.contains(iname); +} + +QSet<QByteArray> WatchHandler::expandedINames() const +{ + return m_model->m_expandedINames; +} + } // namespace Internal } // namespace Debugger + +#include "watchhandler.moc" diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index c99fe88685..db44eef08c 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -35,28 +35,38 @@ #include "watchdata.h" -#include <QPointer> #include <QHash> #include <QSet> #include <QStringList> #include <QAbstractItemModel> namespace Debugger { + class DebuggerEngine; namespace Internal { -class WatchItem; -class WatchHandler; +class WatchModel; + +class UpdateParameters +{ +public: + UpdateParameters() { tryPartial = tooltipOnly = false; } + + bool tryPartial; + bool tooltipOnly; + QByteArray varList; +}; + typedef QHash<QString, QStringList> TypeFormats; enum WatchType { - ReturnWatch, - LocalsWatch, - WatchersWatch, - TooltipsWatch, - InspectWatch + LocalsType, + InspectType, + WatchersType, + ReturnType, + TooltipType }; enum IntegerFormat @@ -67,126 +77,38 @@ enum IntegerFormat OctalFormat }; -class WatchModel : public QAbstractItemModel -{ - Q_OBJECT - -private: - explicit WatchModel(WatchHandler *handler, WatchType type); - virtual ~WatchModel(); - -public: - virtual int rowCount(const QModelIndex &idx = QModelIndex()) const; - virtual int columnCount(const QModelIndex &idx) const; - -signals: - void setCurrentIndex(const QModelIndex &index); - -private: - 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; - 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); - - void invalidateAll(const QModelIndex &parentIndex = QModelIndex()); - - friend class WatchHandler; - - 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); - void reinsertAllData(); - void reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data); - void insertBulkData(const QList<WatchData> &data); - WatchItem *findItem(const QByteArray &iname, WatchItem *root) const; - QString displayForAutoTest(const QByteArray &iname) const; - void reinitialize(); - void removeOutdated(); - void removeOutdatedHelper(WatchItem *item); - WatchItem *rootItem() const; - void destroyItem(WatchItem *item); - - void emitDataChanged(int column, - const QModelIndex &parentIndex = QModelIndex()); - void beginCycle(bool fullCycle); // Called at begin of updateLocals() cycle. - void endCycle(); // Called after all results have been received. - - friend QDebug operator<<(QDebug d, const WatchModel &m); - - void dump(); - void dumpHelper(WatchItem *item); - void emitAllChanged(); - -private: - QString displayType(const WatchData &typeIn) const; - QString formattedValue(const WatchData &data) const; - QString removeInitialNamespace(QString str) const; - QString removeNamespaces(QString str) const; - void formatRequests(QByteArray *out, const WatchItem *item) const; - DebuggerEngine *engine() const; - QString display(const WatchItem *item, int col) const; - int itemFormat(const WatchData &data) const; - bool contentIsValid() const; - int m_generationCounter; - - WatchHandler *m_handler; - WatchType m_type; - WatchItem *m_root; - QSet<QByteArray> m_fetchTriggered; -}; - class WatchHandler : public QObject { Q_OBJECT public: explicit WatchHandler(DebuggerEngine *engine); - WatchModel *model(WatchType type) const; - WatchModel *modelForIName(const QByteArray &iname) const; + ~WatchHandler(); + + QAbstractItemModel *model() const; void cleanup(); void watchExpression(const QString &exp); void removeWatchExpression(const QString &exp); Q_SLOT void clearWatches(); - Q_SLOT void emitAllChanged(); - void beginCycle(bool fullCycle = true); // Called at begin of updateLocals() cycle void updateWatchers(); // Called after locals are fetched - void endCycle(); // Called after all results have been received - - void beginCycle(WatchType type, bool fullCycle = true); - void endCycle(WatchType type); void showEditValue(const WatchData &data); - void insertData(const WatchData &data); - void insertBulkData(const QList<WatchData> &data); - void removeData(const QByteArray &iname); - Q_SLOT void reinsertAllData(); - - const WatchData *watchData(WatchType type, const QModelIndex &) const; - const WatchData *findItem(const QByteArray &iname) const; + const WatchData *watchData(const QModelIndex &) const; + const WatchData *findData(const QByteArray &iname) const; QString displayForAutoTest(const QByteArray &iname) const; - QModelIndex itemIndex(const QByteArray &iname) const; + bool hasItem(const QByteArray &iname) const; void loadSessionData(); void saveSessionData(); void removeTooltip(); void rebuildModel(); - bool isExpandedIName(const QByteArray &iname) const - { return m_expandedINames.contains(iname); } - QSet<QByteArray> expandedINames() const - { return m_expandedINames; } + bool isExpandedIName(const QByteArray &iname) const; + QSet<QByteArray> expandedINames() const; + static QStringList watchedExpressions(); static QHash<QByteArray, int> watcherNames(); @@ -199,7 +121,6 @@ public: void addTypeFormats(const QByteArray &type, const QStringList &formats); void setTypeFormats(const TypeFormats &typeFormats); TypeFormats typeFormats() const; - QStringList typeFormatList(const WatchData &data) const; void setUnprintableBase(int base); static int unprintableBase(); @@ -213,7 +134,15 @@ public: void resetLocation(); bool isValidToolTip(const QByteArray &iname) const; - void setCurrentModelIndex(WatchType modelType, const QModelIndex &index); + void setCurrentItem(const QByteArray &iname); + void updateWatchersWindow(); + + void insertData(const WatchData &data); // Convenience. + void insertData(const QList<WatchData> &list); + void insertIncompleteData(const WatchData &data); + void removeData(const QByteArray &iname); + void removeChildren(const QByteArray &iname); + void removeAllData(); private: friend class WatchModel; @@ -223,25 +152,8 @@ private: static void saveTypeFormats(); void setFormat(const QByteArray &type, int format); - void updateWatchersWindow(); - void showInEditorHelper(QString *contents, WatchItem *item, int level); - - bool m_inChange; - // QWidgets and QProcesses taking care of special displays. - typedef QMap<QByteArray, QPointer<QObject> > EditHandlers; - EditHandlers m_editHandlers; - - TypeFormats m_reportedTypeFormats; - - // Items expanded in the Locals & Watchers view. - QSet<QByteArray> m_expandedINames; - - WatchModel *m_return; - WatchModel *m_locals; - WatchModel *m_watchers; - WatchModel *m_tooltips; - WatchModel *m_inspect; + WatchModel *m_model; DebuggerEngine *m_engine; int m_watcherCounter; @@ -253,4 +165,6 @@ private: } // namespace Internal } // namespace Debugger +Q_DECLARE_METATYPE(Debugger::Internal::UpdateParameters) + #endif // DEBUGGER_WATCHHANDLER_H diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index 9f5ae27609..0cdac8da9c 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -530,12 +530,12 @@ void WatchTreeView::keyPressEvent(QKeyEvent *ev) QString exp = model()->data(idx1).toString(); watchExpression(exp); } - QTreeView::keyPressEvent(ev); + BaseTreeView::keyPressEvent(ev); } void WatchTreeView::dragEnterEvent(QDragEnterEvent *ev) { - //QTreeView::dragEnterEvent(ev); + //BaseTreeView::dragEnterEvent(ev); if (ev->mimeData()->hasText()) { ev->setDropAction(Qt::CopyAction); ev->accept(); @@ -544,7 +544,7 @@ void WatchTreeView::dragEnterEvent(QDragEnterEvent *ev) void WatchTreeView::dragMoveEvent(QDragMoveEvent *ev) { - //QTreeView::dragMoveEvent(ev); + //BaseTreeView::dragMoveEvent(ev); if (ev->mimeData()->hasText()) { ev->setDropAction(Qt::CopyAction); ev->accept(); @@ -559,7 +559,7 @@ void WatchTreeView::dropEvent(QDropEvent *ev) ev->setDropAction(Qt::CopyAction); ev->accept(); } - //QTreeView::dropEvent(ev); + //BaseTreeView::dropEvent(ev); } void WatchTreeView::mouseDoubleClickEvent(QMouseEvent *ev) @@ -570,7 +570,7 @@ void WatchTreeView::mouseDoubleClickEvent(QMouseEvent *ev) watchExpression(QString()); return; } - QTreeView::mouseDoubleClickEvent(ev); + BaseTreeView::mouseDoubleClickEvent(ev); } // Text for add watch action with truncated expression. @@ -971,7 +971,7 @@ bool WatchTreeView::event(QEvent *ev) releaseMouse(); currentEngine()->watchPoint(mapToGlobal(mev->pos())); } - return QTreeView::event(ev); + return BaseTreeView::event(ev); } void WatchTreeView::editItem(const QModelIndex &idx) @@ -982,6 +982,7 @@ void WatchTreeView::editItem(const QModelIndex &idx) void WatchTreeView::setModel(QAbstractItemModel *model) { BaseTreeView::setModel(model); + setRootIndex(model->index(m_type, 0, QModelIndex())); setRootIsDecorated(true); if (header()) { header()->setDefaultAlignment(Qt::AlignLeft); @@ -990,15 +991,25 @@ void WatchTreeView::setModel(QAbstractItemModel *model) } connect(model, SIGNAL(layoutChanged()), SLOT(resetHelper())); - - QTC_ASSERT(qobject_cast<WatchModel*>(model), return); - connect(model, SIGNAL(setCurrentIndex(QModelIndex)), + connect(model, SIGNAL(currentIndexRequested(QModelIndex)), SLOT(setCurrentIndex(QModelIndex))); + connect(model, SIGNAL(itemIsExpanded(QModelIndex)), + SLOT(handleItemIsExpanded(QModelIndex))); +} + +void WatchTreeView::handleItemIsExpanded(const QModelIndex &idx) +{ + bool on = idx.data(LocalsExpandedRole).toBool(); + QTC_ASSERT(on, return); + if (!isExpanded(idx)) + expand(idx); } void WatchTreeView::resetHelper() { - resetHelper(model()->index(0, 0)); + QModelIndex idx = model()->index(m_type, 0); + resetHelper(idx); + expand(idx); } void WatchTreeView::resetHelper(const QModelIndex &idx) @@ -1019,6 +1030,13 @@ void WatchTreeView::resetHelper(const QModelIndex &idx) } } +void WatchTreeView::reset() +{ + BaseTreeView::reset(); + setRootIndex(model()->index(m_type, 0)); + resetHelper(); +} + void WatchTreeView::watchExpression(const QString &exp) { currentEngine()->watchHandler()->watchExpression(exp); @@ -1063,7 +1081,5 @@ void WatchTreeView::setWatchpointAtExpression(const QString &exp) breakHandler()->appendBreakpoint(data); } - } // namespace Internal } // namespace Debugger - diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h index 9a28f6380e..17760dda73 100644 --- a/src/plugins/debugger/watchwindow.h +++ b/src/plugins/debugger/watchwindow.h @@ -49,15 +49,17 @@ class WatchTreeView : public BaseTreeView Q_OBJECT public: - enum Type { ReturnType, LocalsType, TooltipType, WatchersType, InspectType }; + enum Type { LocalsType, InspectType, WatchersType, ReturnType, TooltipType }; explicit WatchTreeView(Type type, QWidget *parent = 0); Type type() const { return m_type; } void setModel(QAbstractItemModel *model); + void reset(); public slots: void watchExpression(const QString &exp); void removeWatchExpression(const QString &exp); + void handleItemIsExpanded(const QModelIndex &idx); private: Q_SLOT void resetHelper(); |