diff --git a/share/qtcreator/dumper/qttypes.py b/share/qtcreator/dumper/qttypes.py index 085f746fa4ef206f72e275f9ebee48fd559c652d..9d00282fbc8ddb15d0ee250ea6a6a0e42a2f4a28 100644 --- a/share/qtcreator/dumper/qttypes.py +++ b/share/qtcreator/dumper/qttypes.py @@ -2397,6 +2397,14 @@ def qdump__Debugger__Internal__GdbMi(d, value): d.putByteArrayValue(value["m_data"]) d.putPlainChildren(value) +def qdump__Debugger__Internal__WatchData(d, value): + d.putByteArrayValue(value["iname"]) + d.putPlainChildren(value) + +def qdump__Debugger__Internal__WatchItem(d, value): + d.putByteArrayValue(value["iname"]) + d.putPlainChildren(value) + def qdump__CPlusPlus__ByteArrayRef(d, value): d.putValue(encodeCharArray(value["m_start"], 100, value["m_length"]), Hex2EncodedLatin1) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 4b2864633a3de7843043ab87e4ce25662ef06590..112ed2524c2bc3e0015b86646b8414d79fc6b6dc 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 897bfd677c0518776fa1032b5c8e10d7b12e5225..57d18dcd403a009c73df564e303c71ef521e7f17 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 017aa11f57329a17495ff52a7224fa4a9f624a1d..4db5bc134c95d6c63ea7447a8269850c3464a32c 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 76089e425bed4a3c9e9e58980124d85259a3e258..c8ff90586a230b578765f4b77721adcd0f68806f 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 bc841121981b6f29c51ee37faa3342840d1dcf35..3efe927e5d4f0c587cd33c32972d4e6db4cc799b 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 cada0ea9d3038226e3f78238187ac765f875de54..fae28bb69c0f28defeeebf4fc79309c310e72b72 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 ccbd610ea3af7759aa3ab6ce605dc218eae21e78..2094d2972397cecbf98e9bab16c47267cab40317 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 a9dd32c0a471b12699071cec44a7d4d28c7b1016..b23fffc2aaddebd8c540238467ad27881c37250a 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 3b2f82a34a82aca7f4f9c5a7090bda851ba5d72e..52b97a172b4de071326930b98b78adaf24d2e858 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 83ee073d05f2e7108ee2dec064740df8f3612992..1a3e87f409f1dd1a440f1c4b2095e0ef30a53822 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 93760ec19cde9a3f868f82badffd6e4f78d0089c..772f5a4d3fb43890ab8a6f116163f591cb095072 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 f69ee66055b6584b1a38ed5e5538bac8c70a4b21..b020cc03b05c63fca85075c09653fbe0965d2199 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 73a01839dea3b5540ea10c5d7e32e632074dc7d7..7586920ba454b8c45487225ed979b2b70d913606 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 4c027110975ab689c3aa926555a4244ff83ad7e7..7683cd8bb391fbeb716661cb3ef87fe21c861078 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 908770961df243a9c34f7b65f552e92c67a7ac82..67078e9781369683b8a3332e3b338f27277c7e2b 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 9cb35e0ffd4c840b71788843502c84e0427956d5..d2c2793be25077b97829621291de8ddccd535f22 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 0d0eafbe62546ff784d939f3d0af8ae72aec16ea..67bd0b82860b7573ee9fcaa8a219c7d72fe456b8 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 1f97e58a542f4941945def1f91f41f00eb3110fc..8d35db37c62620fd658aa9f6bea3ea9de49e898c 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 e4fe415cdd002ed1ce541040f13d3cd2f60d34b6..4b60edc7cb0ccfe25dd0456f7026e5600aba84a1 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 16d758f6c90cf770a672e04f85ff1474ed5656cc..efe52b8d41dd9ed5c0992746a76352460b424a67 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 be6efdcaad03469307a727250a0e72b2aa2e89a9..e9d35176266447a46b47f55ec9e7892f4726181e 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 44f9f9762406de5188491590d48cb28004df32aa..2b63c0b71a215fba046f30184cf324b1a5025ad8 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 c9ada1d5b36facec9ac7a95ffa974557ca7db649..77bc4f9db5703d456f1c2afae918a2c8a21249f7 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 c99fe88685c4ff78e290794fe8203f947f8ee043..db44eef08cd05522833127acaea14df74ac7d49c 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 9f5ae276091e99e74736ce6beb362681c8158e29..0cdac8da9cafce37284bcd700492a86f6f909cc4 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 9a28f6380eb67273c898d63ea26bebe46dc95324..17760dda73bdf94ba4cc7192bde08f0ab800cb3b 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();