From f0b2d6535e7fb8940e83bc3117c6681cd2f80a91 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 10 Jul 2014 18:10:56 +0200 Subject: [PATCH] Debugger: Rework editor tooltips handling Fix expansion and updating. Persistence and non-locals are still lacking. Change-Id: I74e25199d50350516afc686a05836e239bfc8acb Reviewed-by: Joerg Bornemann Reviewed-by: hjk --- src/plugins/debugger/cdb/cdbengine.cpp | 4 +- src/plugins/debugger/debuggercore.h | 2 - src/plugins/debugger/debuggerengine.cpp | 6 + src/plugins/debugger/debuggerplugin.cpp | 5 +- src/plugins/debugger/debuggerrunner.cpp | 5 +- .../debugger/debuggertooltipmanager.cpp | 889 +++++++----------- src/plugins/debugger/debuggertooltipmanager.h | 31 +- src/plugins/debugger/gdb/gdbengine.cpp | 96 +- src/plugins/debugger/gdb/gdbengine.h | 4 +- src/plugins/debugger/lldb/lldbengine.cpp | 64 +- src/plugins/debugger/lldb/lldbengine.h | 3 +- src/plugins/debugger/watchhandler.cpp | 6 +- src/plugins/debugger/watchwindow.cpp | 34 +- src/plugins/debugger/watchwindow.h | 2 +- 14 files changed, 462 insertions(+), 689 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index b9b80a3efa..9fcc912688 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -1968,8 +1968,10 @@ void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply) foreach (const WatchData &wd, watchData) nsp << wd.toString() <<'\n'; } - if (flags & LocalsUpdateForNewFrame) + if (flags & LocalsUpdateForNewFrame) { emit stackFrameCompleted(); + DebuggerToolTipManager::updateEngine(this); + } } else { showMessage(QString::fromLatin1(reply->errorMessage), LogWarning); } diff --git a/src/plugins/debugger/debuggercore.h b/src/plugins/debugger/debuggercore.h index b53c818949..81c2408ddf 100644 --- a/src/plugins/debugger/debuggercore.h +++ b/src/plugins/debugger/debuggercore.h @@ -60,7 +60,6 @@ class BreakHandler; class SnapshotHandler; class Symbol; class Section; -class DebuggerToolTipManager; class GlobalDebuggerOptions; enum TestCases @@ -120,7 +119,6 @@ public: virtual QStringList stringListSetting(int code) const = 0; virtual void setThreads(const QStringList &list, int index) = 0; - virtual DebuggerToolTipManager *toolTipManager() const = 0; virtual QSharedPointer globalDebuggerOptions() const = 0; static QTreeView *inspectorView(); diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index f1d1c89d33..9ccd0f0b0b 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -35,6 +35,7 @@ #include "debuggerrunner.h" #include "debuggerstringutils.h" #include "debuggerstartparameters.h" +#include "debuggertooltipmanager.h" #include "breakhandler.h" #include "disassembleragent.h" @@ -1176,11 +1177,16 @@ void DebuggerEngine::setState(DebuggerState state, bool forced) if (!forced && !isAllowedTransition(oldState, state)) qDebug() << "*** UNEXPECTED STATE TRANSITION: " << this << msg; + if (state == EngineRunRequested) { + DebuggerToolTipManager::registerEngine(this); + } + if (state == DebuggerFinished) { // Give up ownership on claimed breakpoints. BreakHandler *handler = breakHandler(); foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) handler->notifyBreakpointReleased(id); + DebuggerToolTipManager::deregisterEngine(this); } showMessage(msg, LogDebug); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index ac39ec1cad..5a3dbd662b 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1202,7 +1202,6 @@ public slots: bool parseArguments(const QStringList &args, QString *errorMessage); void parseCommandLineArguments(); - DebuggerToolTipManager *toolTipManager() const { return m_toolTipManager; } QSharedPointer globalDebuggerOptions() const { return m_globalDebuggerOptions; } void updateQmlActions() { @@ -2526,7 +2525,7 @@ void DebuggerPluginPrivate::sessionLoaded() { m_breakHandler->loadSessionData(); dummyEngine()->watchHandler()->loadSessionData(); - m_toolTipManager->loadSessionData(); + DebuggerToolTipManager::loadSessionData(); } void DebuggerPluginPrivate::aboutToUnloadSession() @@ -2537,8 +2536,8 @@ void DebuggerPluginPrivate::aboutToUnloadSession() void DebuggerPluginPrivate::aboutToSaveSession() { dummyEngine()->watchHandler()->saveSessionData(); - m_toolTipManager->saveSessionData(); m_breakHandler->saveSessionData(); + DebuggerToolTipManager::saveSessionData(); } void DebuggerPluginPrivate::showStatusMessage(const QString &msg0, int timeout) diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp index 1b30a6aba5..fcee2fedc2 100644 --- a/src/plugins/debugger/debuggerrunner.cpp +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -38,7 +38,6 @@ #include "debuggerrunconfigurationaspect.h" #include "debuggerstartparameters.h" #include "debuggerstringutils.h" -#include "debuggertooltipmanager.h" #include "breakhandler.h" #include "shared/peutils.h" @@ -136,9 +135,7 @@ DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfiguration, QString errorMessage; d->m_engine = DebuggerRunControlFactory::createEngine(sp.masterEngineType, sp, &errorMessage); - if (d->m_engine) { - DebuggerToolTipManager::registerEngine(d->m_engine); - } else { + if (!d->m_engine) { debuggingFinished(); Core::ICore::showWarningWithOptions(DebuggerRunControl::tr("Debugger"), errorMessage); } diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp index 3627bcdb4c..b4034c522f 100644 --- a/src/plugins/debugger/debuggertooltipmanager.cpp +++ b/src/plugins/debugger/debuggertooltipmanager.cpp @@ -33,6 +33,9 @@ #include "debuggeractions.h" #include "stackhandler.h" #include "debuggercore.h" +#include "watchhandler.h" +#include "watchwindow.h" +#include "sourceutils.h" #include #include @@ -61,9 +64,6 @@ using namespace Core; using namespace TextEditor; -enum { debugToolTips = 0 }; -enum { debugToolTipPositioning = 0 }; - // Expire tooltips after n days on (no longer load them) in order // to avoid them piling up. enum { toolTipsExpiryDays = 6 }; @@ -94,9 +94,6 @@ static const char modelItemElementC[] = "item"; // next start element of a desired type. static bool readStartElement(QXmlStreamReader &r, const char *name) { - if (debugToolTips > 1) - qDebug("readStartElement: looking for '%s', currently at: %s/%s", - name, qPrintable(r.tokenString()), qPrintable(r.name().toString())); while (r.tokenType() != QXmlStreamReader::StartElement || r.name() != QLatin1String(name)) switch (r.readNext()) { @@ -127,38 +124,29 @@ static void debugMode(const QAbstractItemModel *model) namespace Debugger { namespace Internal { -/* A Label that emits a signal when the user drags for moving the parent - * widget around. */ +// A label that can be dragged to drag something else. + class DraggableLabel : public QLabel { - Q_OBJECT public: - explicit DraggableLabel(QWidget *parent = 0); - - bool isActive() const { return m_active; } - void setActive(bool v) { m_active = v; } + explicit DraggableLabel(QWidget *target) + : m_target(target), m_moveStartPos(-1, -1), active(false) + {} -signals: - void dragged(const QPoint &d); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); -protected: - virtual void mousePressEvent(QMouseEvent * event); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - -private: +public: + QWidget *m_target; QPoint m_moveStartPos; - bool m_active; + QPoint m_offset; + bool active; }; -DraggableLabel::DraggableLabel(QWidget *parent) : - QLabel(parent), m_moveStartPos(-1, -1), m_active(false) -{ -} - void DraggableLabel::mousePressEvent(QMouseEvent * event) { - if (m_active && event->button() == Qt::LeftButton) { + if (active && event->button() == Qt::LeftButton) { m_moveStartPos = event->globalPos(); event->accept(); } @@ -167,17 +155,21 @@ void DraggableLabel::mousePressEvent(QMouseEvent * event) void DraggableLabel::mouseReleaseEvent(QMouseEvent * event) { - if (m_active && event->button() == Qt::LeftButton) + if (active && event->button() == Qt::LeftButton) m_moveStartPos = QPoint(-1, -1); QLabel::mouseReleaseEvent(event); } void DraggableLabel::mouseMoveEvent(QMouseEvent * event) { - if (m_active && (event->buttons() & Qt::LeftButton)) { + if (active && (event->buttons() & Qt::LeftButton)) { if (m_moveStartPos != QPoint(-1, -1)) { const QPoint newPos = event->globalPos(); - emit dragged(event->globalPos() - m_moveStartPos); + const QPoint offset = newPos - m_moveStartPos; + + m_target->move(m_target->pos() + offset); + m_offset += offset; + m_moveStartPos = newPos; } event->accept(); @@ -191,7 +183,7 @@ void DraggableLabel::mouseMoveEvent(QMouseEvent * event) class DebuggerToolTipEditor { public: - explicit DebuggerToolTipEditor(IEditor *ie = 0); + explicit DebuggerToolTipEditor(IEditor *ie); bool isValid() const { return editor; } QString fileName() const { return editor->document() ? editor->document()->filePath() : QString(); } @@ -274,8 +266,8 @@ void StandardItemTreeModelBuilder::endRow() m_rowParents.pop(); } -/* Helper visitor base class for recursing over a tree model - * (see StandardItemTreeModelBuilder for the scheme). */ +// Helper visitor base class for recursing over a tree model +// (see StandardItemTreeModelBuilder for the scheme). class TreeModelVisitor { public: @@ -455,11 +447,7 @@ static QDebug operator<<(QDebug d, const QAbstractItemModel &model) class TooltipFilterModel : public QSortFilterProxyModel { public: - TooltipFilterModel(QAbstractItemModel *model, const QByteArray &iname) - : m_iname(iname) - { - setSourceModel(model); - } + TooltipFilterModel() {} QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { @@ -478,11 +466,12 @@ public: { const QModelIndex nameIndex = sourceModel()->index(sourceRow, 0, sourceParent); const QByteArray iname = nameIndex.data(LocalsINameRole).toByteArray(); +// qDebug() << "ACCEPTING FILTER" << iname +// << (iname == m_iname || isSubIname(iname, m_iname) || isSubIname(m_iname, iname)); return iname == m_iname || isSubIname(iname, m_iname) || isSubIname(m_iname, iname); } -private: - const QByteArray m_iname; + QByteArray m_iname; }; ///////////////////////////////////////////////////////////////////////// @@ -507,6 +496,29 @@ private: StandardItemTreeModelBuilder m_builder; }; +class DebuggerToolTipWidget; +class DebuggerToolTipManagerData +{ +public: + DebuggerToolTipManagerData() + : m_debugModeActive(false), m_lastToolTipPoint(-1, -1), m_lastToolTipEditor(0) + {} + + void purgeClosedToolTips() + { + for (int i = m_tooltips.size(); --i >= 0; ) + if (!m_tooltips.at(i)) + m_tooltips.removeAt(i); + } + + QList > m_tooltips; + bool m_debugModeActive; + QPoint m_lastToolTipPoint; + Core::IEditor *m_lastToolTipEditor; +}; + +static DebuggerToolTipManagerData *d = 0; + ///////////////////////////////////////////////////////////////////////// // @@ -519,59 +531,46 @@ class DebuggerToolTipWidget : public QWidget Q_OBJECT public: - bool isPinned() const { return m_isPinned; } - - explicit DebuggerToolTipWidget(QWidget *parent = 0); - bool engineAcquired() const { return m_engineAcquired; } + DebuggerToolTipWidget(const DebuggerToolTipContext &context); + bool isPinned() const { return m_isPinned; } QString fileName() const { return m_context.fileName; } QString function() const { return m_context.function; } int position() const { return m_context.position; } - // Check for a match at position. - bool matches(const QString &fileName, - const QString &engineType = QString(), - const QString &function= QString()) const; const DebuggerToolTipContext &context() const { return m_context; } - void setContext(const DebuggerToolTipContext &c) { m_context = c; } - QString engineType() const { return m_engineType; } - void setEngineType(const QString &e) { m_engineType = e; } + void acquireEngine(); + void releaseEngine(); - QDate creationDate() const { return m_creationDate; } - void setCreationDate(const QDate &d) { m_creationDate = d; } + void saveSessionData(QXmlStreamWriter &w) const; + void setWatchModel(QAbstractItemModel *watchModel); + void handleStackFrameCompleted(const QString &frameFile, const QString &frameFunction); public slots: - void saveSessionData(QXmlStreamWriter &w) const; + void handleItemIsExpanded(const QModelIndex &sourceIdx) + { + QTC_ASSERT(m_filterModel.sourceModel() == sourceIdx.model(), return); + QModelIndex mappedIdx = m_filterModel.mapFromSource(sourceIdx); + if (!m_treeView->isExpanded(mappedIdx)) + m_treeView->expand(mappedIdx); + } - void acquireEngine(Debugger::DebuggerEngine *engine); - void releaseEngine(); void copy(); - bool positionShow(const DebuggerToolTipEditor &pe); + void positionShow(const DebuggerToolTipEditor &pe); void pin(); - void doLoadSessionData(QXmlStreamReader &r); - -private slots: - void slotDragged(const QPoint &p); void toolButtonClicked(); private: - void doReleaseEngine(); - void doSaveSessionData(QXmlStreamWriter &w) const; - QString clipboardContents() const; - QAbstractItemModel *swapModel(QAbstractItemModel *newModel); - +public: bool m_isPinned; QToolButton *m_toolButton; DraggableLabel *m_titleLabel; - bool m_engineAcquired; - QString m_engineType; - DebuggerToolTipContext m_context; QDate m_creationDate; - QPoint m_offset; //!< Offset to text cursor position (user dragging). - int m_debuggerModel; - DebuggerToolTipTreeView *m_treeView; - QStandardItemModel *m_defaultModel; + DebuggerToolTipTreeView *m_treeView; //!< Pointing to either m_defaultModel oder m_filterModel + DebuggerToolTipContext m_context; + TooltipFilterModel m_filterModel; //!< Pointing to a valid watchModel + QStandardItemModel m_defaultModel; }; void DebuggerToolTipWidget::pin() @@ -589,7 +588,7 @@ void DebuggerToolTipWidget::pin() // We have just be restored from session data. setWindowFlags(Qt::ToolTip); } - m_titleLabel->setActive(true); // User can now drag + m_titleLabel->active = true; // User can now drag } void DebuggerToolTipWidget::toolButtonClicked() @@ -611,27 +610,28 @@ void DebuggerToolTipWidget::toolButtonClicked() on restoring. */ -DebuggerToolTipContext::DebuggerToolTipContext() : position(0), line(0), column(0) +DebuggerToolTipContext::DebuggerToolTipContext() + : position(0), line(0), column(0) { } -DebuggerToolTipContext DebuggerToolTipContext::fromEditor(IEditor *ie, int pos) +bool DebuggerToolTipContext::matchesFrame(const QString &frameFile, const QString &frameFunction) const { - DebuggerToolTipContext rc; - if (const IDocument *document = ie->document()) { - if (const ITextEditor *te = qobject_cast(ie)) { - rc.fileName = document->filePath(); - rc.position = pos; - te->convertPosition(pos, &rc.line, &rc.column); - } - } - return rc; + return (fileName.isEmpty() || frameFile.isEmpty() || fileName == frameFile) + && (function.isEmpty() || frameFunction.isEmpty() || function == frameFunction); +} + +bool DebuggerToolTipContext::isSame(const DebuggerToolTipContext &other) const +{ + return fileName == other.fileName + && function == other.function + && iname == other.iname; } QDebug operator<<(QDebug d, const DebuggerToolTipContext &c) { QDebug nsp = d.nospace(); - nsp << c.fileName << '@' << c.line << ',' << c.column << " (" << c.position << ')'; + nsp << c.fileName << '@' << c.line << ',' << c.column << " (" << c.position << ')' << "INAME: " << c.iname << " EXP: " << c.expression; if (!c.function.isEmpty()) nsp << ' ' << c.function << "()"; return d; @@ -678,146 +678,189 @@ QDebug operator<<(QDebug d, const DebuggerToolTipContext &c) static QString msgReleasedText() { return DebuggerToolTipWidget::tr("Previous"); } -DebuggerToolTipWidget::DebuggerToolTipWidget(QWidget *parent) : - QWidget(parent), - m_isPinned(false), - m_toolButton(new QToolButton), - m_titleLabel(new DraggableLabel), - m_engineAcquired(false), - m_creationDate(QDate::currentDate()), - m_treeView(new DebuggerToolTipTreeView(this)), - m_defaultModel(new QStandardItemModel(this)) +DebuggerToolTipWidget::DebuggerToolTipWidget(const DebuggerToolTipContext &context) { + setFocusPolicy(Qt::NoFocus); + + m_isPinned = false; + m_context = context; + m_filterModel.m_iname = context.iname; + const QIcon pinIcon(QLatin1String(":/debugger/images/pin.xpm")); - const QList pinIconSizes = pinIcon.availableSizes(); + m_toolButton = new QToolButton; m_toolButton->setIcon(pinIcon); - connect(m_toolButton, SIGNAL(clicked()), this, SLOT(toolButtonClicked())); - QToolButton *copyButton = new QToolButton; + auto copyButton = new QToolButton; copyButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_COPY))); - connect(copyButton, SIGNAL(clicked()), this, SLOT(copy())); + m_titleLabel = new DraggableLabel(this); m_titleLabel->setText(msgReleasedText()); m_titleLabel->setMinimumWidth(40); // Ensure a draggable area even if text is empty. - connect(m_titleLabel, SIGNAL(dragged(QPoint)), this, SLOT(slotDragged(QPoint))); - QToolBar *toolBar = new QToolBar(this); + auto toolBar = new QToolBar(this); toolBar->setProperty("_q_custom_style_disabled", QVariant(true)); + const QList pinIconSizes = pinIcon.availableSizes(); if (!pinIconSizes.isEmpty()) toolBar->setIconSize(pinIconSizes.front()); toolBar->addWidget(m_toolButton); toolBar->addWidget(m_titleLabel); toolBar->addWidget(copyButton); + m_treeView = new DebuggerToolTipTreeView(this); m_treeView->setFocusPolicy(Qt::NoFocus); - QVBoxLayout *mainLayout = new QVBoxLayout(this); + auto mainLayout = new QVBoxLayout(this); mainLayout->setSizeConstraint(QLayout::SetFixedSize); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(toolBar); mainLayout->addWidget(m_treeView); + + connect(m_toolButton, SIGNAL(clicked()), this, SLOT(toolButtonClicked())); + connect(copyButton, SIGNAL(clicked()), this, SLOT(copy())); } -bool DebuggerToolTipWidget::matches(const QString &fileName, - const QString &engineType, - const QString &function) const +void DebuggerToolTipWidget::setWatchModel(QAbstractItemModel *watchModel) { - if (fileName.isEmpty() || m_context.fileName != fileName) - return false; - // Optional. - if (!engineType.isEmpty() && engineType != m_engineType) - return false; - if (function.isEmpty() || m_context.function.isEmpty()) - return true; - return function == m_context.function; + QTC_ASSERT(watchModel, return); + m_filterModel.setSourceModel(watchModel); + connect(watchModel, SIGNAL(itemIsExpanded(QModelIndex)), + this, SLOT(handleItemIsExpanded(QModelIndex)), Qt::UniqueConnection); + connect(watchModel, SIGNAL(columnAdjustmentRequested()), + m_treeView, SLOT(computeSize()), Qt::UniqueConnection); } -void DebuggerToolTipWidget::acquireEngine(DebuggerEngine *engine) +void DebuggerToolTipWidget::handleStackFrameCompleted(const QString &frameFile, const QString &frameFunction) { - QTC_ASSERT(engine, return); - - if (debugToolTips) - qDebug() << this << " acquiring" << engine << m_engineAcquired; - if (m_engineAcquired) - return; + const bool sameFrame = m_context.matchesFrame(frameFile, frameFunction); + const bool isAcquired = m_treeView->model() == &m_filterModel; + if (isAcquired && !sameFrame) + releaseEngine(); + else if (!isAcquired && sameFrame) + acquireEngine(); - m_engineType = engine->objectName(); - m_engineAcquired = true; - m_titleLabel->setText(QString()); + if (isAcquired) { + m_treeView->expand(m_filterModel.index(0, 0)); + WatchTreeView::reexpand(m_treeView, m_filterModel.index(0, 0)); + } +} - // Create a filter model on the debugger's model and switch to it. - QAbstractItemModel *model = engine->watchModel(); - TooltipFilterModel *filterModel = new TooltipFilterModel(model, m_context.iname); - swapModel(filterModel); +void DebuggerToolTipWidget::acquireEngine() +{ + m_titleLabel->setText(m_context.expression); + m_treeView->setModel(&m_filterModel); + m_treeView->setRootIndex(m_filterModel.index(0, 0)); + m_treeView->expand(m_filterModel.index(0, 0)); + WatchTreeView::reexpand(m_treeView, m_filterModel.index(0, 0)); } void DebuggerToolTipWidget::releaseEngine() { - // Release engine of same type - if (!m_engineAcquired) - return; - if (debugToolTips) - qDebug() << "releaseEngine" << this; - doReleaseEngine(); + // Save data to stream and restore to the backup m_defaultModel. + m_defaultModel.removeRows(0, m_defaultModel.rowCount()); + TreeModelCopyVisitor v(&m_filterModel, &m_defaultModel); + v.run(); + m_titleLabel->setText(msgReleasedText()); - m_engineAcquired = false; + m_treeView->setModel(&m_defaultModel); + m_treeView->setRootIndex(m_defaultModel.index(0, 0)); + m_treeView->expandAll(); } void DebuggerToolTipWidget::copy() { - const QString clipboardText = clipboardContents(); + QString clipboardText = DebuggerToolTipManager::treeModelClipboardContents(m_treeView->model()); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(clipboardText, QClipboard::Selection); clipboard->setText(clipboardText, QClipboard::Clipboard); } -void DebuggerToolTipWidget::slotDragged(const QPoint &p) -{ - move(pos() + p); - m_offset += p; -} - -bool DebuggerToolTipWidget::positionShow(const DebuggerToolTipEditor &te) +void DebuggerToolTipWidget::positionShow(const DebuggerToolTipEditor &te) { // Figure out new position of tooltip using the text edit. // If the line changed too much, close this tip. - QTC_ASSERT(te.isValid(), return false); + QTC_ASSERT(te.isValid(), return); QTextCursor cursor(te.widget->document()); cursor.setPosition(m_context.position); const int line = cursor.blockNumber(); if (qAbs(m_context.line - line) > 2) { - if (debugToolTips) - qDebug() << "Closing " << this << " in positionShow() lines " - << line << m_context.line; close(); - return false; + return ; } - if (debugToolTipPositioning) - qDebug() << "positionShow" << this << line << cursor.columnNumber(); - const QPoint screenPos = te.widget->toolTipPosition(cursor) + m_offset; + const QPoint screenPos = te.widget->toolTipPosition(cursor) + m_titleLabel->m_offset; const QRect toolTipArea = QRect(screenPos, QSize(sizeHint())); const QRect plainTextArea = QRect(te.widget->mapToGlobal(QPoint(0, 0)), te.widget->size()); - const bool visible = plainTextArea.contains(toolTipArea); - if (debugToolTips) - qDebug() << "DebuggerToolTipWidget::positionShow() " << this << m_context - << " line: " << line << " plainTextPos " << toolTipArea - << " offset: " << m_offset - << " Area: " << plainTextArea << " Screen pos: " - << screenPos << te.widget << " visible=" << visible; + const bool visible = plainTextArea.intersects(toolTipArea); + // qDebug() << "DebuggerToolTipWidget::positionShow() " << this << m_context + // << " line: " << line << " plainTextPos " << toolTipArea + // << " offset: " << m_titleLabel->m_offset + // << " Area: " << plainTextArea << " Screen pos: " + // << screenPos << te.widget << " visible=" << visible; if (!visible) { hide(); - return false; + return; } move(screenPos); show(); - return true; } - // Parse a 'yyyyMMdd' date +static DebuggerToolTipWidget *findOrCreateWidget(const DebuggerToolTipContext &context) +{ + foreach (const QPointer &tw, d->m_tooltips) + if (tw && tw->m_context.isSame(context)) + return tw; + + auto tw = new DebuggerToolTipWidget(context); + tw->setAttribute(Qt::WA_DeleteOnClose); + tw->setObjectName(QLatin1String("DebuggerTreeViewToolTipWidget: ") + QLatin1String(context.iname)); + tw->m_context.creationDate = QDate::currentDate(); + + d->m_tooltips.push_back(tw); + + return tw; +} + +static void restoreTreeModel(QXmlStreamReader &r, QStandardItemModel *m) +{ + StandardItemTreeModelBuilder builder(m); + int columnCount = 1; + bool withinModel = true; + while (withinModel && !r.atEnd()) { + const QXmlStreamReader::TokenType token = r.readNext(); + switch (token) { + case QXmlStreamReader::StartElement: { + const QStringRef element = r.name(); + // Root model element with column count. + if (element == QLatin1String(modelElementC)) { + if (const int cc = r.attributes().value(QLatin1String(modelColumnCountAttributeC)).toString().toInt()) + columnCount = cc; + m->setColumnCount(columnCount); + } else if (element == QLatin1String(modelRowElementC)) { + builder.startRow(); + } else if (element == QLatin1String(modelItemElementC)) { + builder.addItem(r.readElementText()); + } + } + break; // StartElement + case QXmlStreamReader::EndElement: { + const QStringRef element = r.name(); + // Row closing: pop off parent. + if (element == QLatin1String(modelRowElementC)) + builder.endRow(); + else if (element == QLatin1String(modelElementC)) + withinModel = false; + } + break; // EndElement + default: + break; + } // switch + } // while +} + +// Parse a 'yyyyMMdd' date static QDate dateFromString(const QString &date) { return date.size() == 8 ? @@ -825,10 +868,10 @@ static QDate dateFromString(const QString &date) QDate(); } -static DebuggerToolTipWidget *loadSessionDataI(QXmlStreamReader &r) +static void loadSessionDataI(QXmlStreamReader &r) { if (!readStartElement(r, toolTipElementC)) - return 0; + return; const QXmlStreamAttributes attributes = r.attributes(); DebuggerToolTipContext context; context.fileName = attributes.value(QLatin1String(fileNameAttributeC)).toString(); @@ -845,32 +888,35 @@ static DebuggerToolTipWidget *loadSessionDataI(QXmlStreamReader &r) offset.setY(attributes.value(offsetYAttribute).toString().toInt()); context.mousePosition = offset; + context.iname = attributes.value(QLatin1String(treeInameAttributeC)).toString().toLatin1(); + context.expression = attributes.value(QLatin1String(treeExpressionAttributeC)).toString(); + const QStringRef className = attributes.value(QLatin1String(toolTipClassAttributeC)); - const QString engineType = attributes.value(QLatin1String(engineTypeAttributeC)).toString(); - const QDate creationDate = dateFromString(attributes.value(QLatin1String(dateAttributeC)).toString()); - if (!creationDate.isValid() || creationDate.daysTo(QDate::currentDate()) > toolTipsExpiryDays) { - if (debugToolTips) - qDebug() << "Expiring tooltip " << context.fileName << '@' << context.position << " from " << creationDate; - r.readElementText(QXmlStreamReader::SkipChildElements); // Skip - return 0; + context.engineType = attributes.value(QLatin1String(engineTypeAttributeC)).toString(); + context.creationDate = dateFromString(attributes.value(QLatin1String(dateAttributeC)).toString()); + bool readTree = context.isValid(); + if (!context.creationDate.isValid() || context.creationDate.daysTo(QDate::currentDate()) > toolTipsExpiryDays) { + // qDebug() << "Expiring tooltip " << context.fileName << '@' << context.position << " from " << creationDate; + //readTree = false; + } else if (className != QLatin1String("Debugger::Internal::DebuggerToolTipWidget")) { + qWarning("Unable to create debugger tool tip widget of class %s", qPrintable(className.toString())); + readTree = false; } - if (debugToolTips) - qDebug() << "Creating tooltip " << context << " from " << creationDate << offset; - DebuggerToolTipWidget *rc = 0; - if (className == QLatin1String("Debugger::Internal::DebuggerToolTipWidget")) - rc = new DebuggerToolTipWidget; - if (rc) { - rc->setContext(context); - rc->setAttribute(Qt::WA_DeleteOnClose); - rc->setEngineType(engineType); - rc->doLoadSessionData(r); - rc->setCreationDate(creationDate); - rc->pin(); + + if (readTree) { + DebuggerToolTipWidget *tw = findOrCreateWidget(context); + restoreTreeModel(r, &tw->m_defaultModel); + tw->pin(); + tw->acquireEngine(); + tw->m_titleLabel->setText(DebuggerToolTipManager::tr("Restored")); + tw->m_treeView->setModel(&tw->m_defaultModel); + tw->m_treeView->setRootIndex(tw->m_defaultModel.index(0, 0)); + tw->m_treeView->expandAll(); } else { - qWarning("Unable to create debugger tool tip widget of class %s", qPrintable(className.toString())); r.readElementText(QXmlStreamReader::SkipChildElements); // Skip } - return rc; + + r.readNext(); // Skip } void DebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const @@ -885,14 +931,20 @@ void DebuggerToolTipWidget::saveSessionData(QXmlStreamWriter &w) const attributes.append(QLatin1String(textLineAttributeC), QString::number(m_context.line)); attributes.append(QLatin1String(textColumnAttributeC), QString::number(m_context.column)); attributes.append(QLatin1String(dateAttributeC), m_creationDate.toString(QLatin1String("yyyyMMdd"))); - if (m_offset.x()) - attributes.append(QLatin1String(offsetXAttributeC), QString::number(m_offset.x())); - if (m_offset.y()) - attributes.append(QLatin1String(offsetYAttributeC), QString::number(m_offset.y())); - if (!m_engineType.isEmpty()) - attributes.append(QLatin1String(engineTypeAttributeC), m_engineType); + if (m_titleLabel->m_offset.x()) + attributes.append(QLatin1String(offsetXAttributeC), QString::number(m_titleLabel->m_offset.x())); + if (m_titleLabel->m_offset.y()) + attributes.append(QLatin1String(offsetYAttributeC), QString::number(m_titleLabel->m_offset.y())); + attributes.append(QLatin1String(engineTypeAttributeC), m_context.engineType); + attributes.append(QLatin1String(treeExpressionAttributeC), m_context.expression); + attributes.append(QLatin1String(treeInameAttributeC), QLatin1String(m_context.iname)); w.writeAttributes(attributes); - doSaveSessionData(w); + + w.writeStartElement(QLatin1String(treeElementC)); + XmlWriterTreeModelVisitor v(&m_filterModel, w); + v.run(); + w.writeEndElement(); + w.writeEndElement(); } @@ -936,31 +988,6 @@ void DebuggerToolTipTreeView::collapseNode(const QModelIndex &idx) model()->setData(idx, false, LocalsExpandedRole); } -void DebuggerToolTipTreeView::handleItemIsExpanded(const QModelIndex &sourceIdx) -{ - QSortFilterProxyModel *filterModel = qobject_cast(model()); - QModelIndex mappedIdx = filterModel ? filterModel->mapFromSource(sourceIdx) : sourceIdx; - if (!isExpanded(mappedIdx)) - expand(mappedIdx); -} - -QAbstractItemModel *DebuggerToolTipTreeView::swapModel(QAbstractItemModel *newModel) -{ - QAbstractItemModel *previousModel = model(); - if (previousModel != newModel) { - if (previousModel) - previousModel->disconnect(SIGNAL(rowsInserted(QModelIndex,int,int)), this); - setModel(newModel); - connect(newModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - SLOT(computeSize()), Qt::QueuedConnection); - if (QSortFilterProxyModel *filterModel = qobject_cast(newModel)) { - connect(filterModel->sourceModel(), SIGNAL(itemIsExpanded(QModelIndex)), - SLOT(handleItemIsExpanded(QModelIndex))); - } - } - return previousModel; -} - int DebuggerToolTipTreeView::computeHeight(const QModelIndex &index) const { int s = rowHeight(index); @@ -972,6 +999,7 @@ int DebuggerToolTipTreeView::computeHeight(const QModelIndex &index) const void DebuggerToolTipTreeView::computeSize() { + WatchTreeView::reexpand(this, model()->index(0, 0)); int columns = 30; // Decoration int rows = 0; bool rootDecorated = false; @@ -1019,122 +1047,18 @@ void DebuggerToolTipTreeView::computeSize() setMinimumSize(m_size); setMaximumSize(m_size); setRootIsDecorated(rootDecorated); - - // This pretty much feels like a hack. - // But it "solves" QTCREATORBUG-9852 - QApplication::processEvents(); - viewport()->update(); } -QAbstractItemModel *DebuggerToolTipWidget::swapModel(QAbstractItemModel *newModel) -{ - QAbstractItemModel *oldModel = m_treeView->swapModel(newModel); - // When looking at some 'this.m_foo.x', expand all items - if (newModel) { - if (const int level = m_context.iname.count('.')) { - QModelIndex index = newModel->index(0, 0); - for (int i = 0; i < level && index.isValid(); i++, index = index.child(0, 0)) - m_treeView->setExpanded(index, true); - } - } - return oldModel; -} - -void DebuggerToolTipWidget::doReleaseEngine() -{ - // Save data to stream and restore to the m_defaultModel (QStandardItemModel) - m_defaultModel->removeRows(0, m_defaultModel->rowCount()); - if (const QAbstractItemModel *model = m_treeView->model()) { - TreeModelCopyVisitor v(model, m_defaultModel); - v.run(); - } - delete swapModel(m_defaultModel); -} - -static void restoreTreeModel(QXmlStreamReader &r, QStandardItemModel *m) -{ - StandardItemTreeModelBuilder builder(m); - int columnCount = 1; - bool withinModel = true; - while (withinModel && !r.atEnd()) { - const QXmlStreamReader::TokenType token = r.readNext(); - switch (token) { - case QXmlStreamReader::StartElement: { - const QStringRef element = r.name(); - // Root model element with column count. - if (element == QLatin1String(modelElementC)) { - if (const int cc = r.attributes().value(QLatin1String(modelColumnCountAttributeC)).toString().toInt()) - columnCount = cc; - m->setColumnCount(columnCount); - } else if (element == QLatin1String(modelRowElementC)) { - builder.startRow(); - } else if (element == QLatin1String(modelItemElementC)) { - builder.addItem(r.readElementText()); - } - } - break; // StartElement - case QXmlStreamReader::EndElement: { - const QStringRef element = r.name(); - // Row closing: pop off parent. - if (element == QLatin1String(modelRowElementC)) - builder.endRow(); - else if (element == QLatin1String(modelElementC)) - withinModel = false; - } - break; // EndElement - default: - break; - } // switch - } // while -} - -void DebuggerToolTipWidget::doSaveSessionData(QXmlStreamWriter &w) const -{ - w.writeStartElement(QLatin1String(treeElementC)); - QXmlStreamAttributes attributes; - if (!m_context.expression.isEmpty()) - attributes.append(QLatin1String(treeExpressionAttributeC), m_context.expression); - attributes.append(QLatin1String(treeInameAttributeC), QLatin1String(m_context.iname)); - w.writeAttributes(attributes); - if (QAbstractItemModel *model = m_treeView->model()) { - XmlWriterTreeModelVisitor v(model, w); - v.run(); - } - w.writeEndElement(); -} - -void DebuggerToolTipWidget::doLoadSessionData(QXmlStreamReader &r) -{ - if (!readStartElement(r, treeElementC)) - return; - // Restore data to default model and show that. - const QXmlStreamAttributes attributes = r.attributes(); - m_context.iname = attributes.value(QLatin1String(treeInameAttributeC)).toString().toLatin1(); - m_context.expression = attributes.value(QLatin1String(treeExpressionAttributeC)).toString(); - if (debugToolTips) - qDebug() << "DebuggerTreeViewToolTipWidget::doLoadSessionData() " << m_debuggerModel << m_context.iname; - setObjectName(QLatin1String("DebuggerTreeViewToolTipWidget: ") + QLatin1String(m_context.iname)); - restoreTreeModel(r, m_defaultModel); - r.readNext(); // Skip - m_treeView->swapModel(m_defaultModel); -} - -QString DebuggerToolTipManager::treeModelClipboardContents(const QAbstractItemModel *m) +QString DebuggerToolTipManager::treeModelClipboardContents(const QAbstractItemModel *model) { QString rc; + QTC_ASSERT(model, return rc); QTextStream str(&rc); - DumpTreeModelVisitor v(m, DumpTreeModelVisitor::ClipboardMode, str); + DumpTreeModelVisitor v(model, DumpTreeModelVisitor::ClipboardMode, str); v.run(); return rc; } -QString DebuggerToolTipWidget::clipboardContents() const -{ - if (const QAbstractItemModel *model = m_treeView->model()) - return DebuggerToolTipManager::treeModelClipboardContents(model); - return QString(); -} - /*! \class Debugger::Internal::DebuggerToolTipManager @@ -1150,27 +1074,6 @@ QString DebuggerToolTipWidget::clipboardContents() const (by file name and function) acquire the engine, others release. */ -typedef QList > DebuggerToolTipWidgetList; -class DebuggerToolTipManagerData -{ -public: - DebuggerToolTipManagerData() - : m_debugModeActive(false), m_lastToolTipPoint(-1, -1), m_lastToolTipEditor(0) - {} - - void registerToolTip(DebuggerToolTipWidget *toolTipWidget); - void moveToolTipsBy(const QPoint &distance); - // Purge out closed (null) tooltips and return list for convenience - void purgeClosedToolTips(); - - DebuggerToolTipWidgetList m_tooltips; - - bool m_debugModeActive; - QPoint m_lastToolTipPoint; - Core::IEditor *m_lastToolTipEditor; -}; - -static DebuggerToolTipManagerData *d = 0; static DebuggerToolTipManager *m_instance = 0; DebuggerToolTipManager::DebuggerToolTipManager(QObject *parent) : @@ -1186,52 +1089,61 @@ DebuggerToolTipManager::~DebuggerToolTipManager() m_instance = 0; } -void DebuggerToolTipManager::registerEngine(DebuggerEngine *engine) +void DebuggerToolTipManager::registerEngine(DebuggerEngine *) { - connect(engine, SIGNAL(stateChanged(Debugger::DebuggerState)), - m_instance, SLOT(slotDebuggerStateChanged(Debugger::DebuggerState))); - connect(engine, SIGNAL(stackFrameCompleted()), - m_instance, SLOT(slotStackFrameCompleted())); + loadSessionData(); } -bool DebuggerToolTipManager::hasToolTips() +void DebuggerToolTipManager::updateEngine(DebuggerEngine *engine) { - return !d->m_tooltips.isEmpty(); -} + QTC_ASSERT(engine, return); + d->purgeClosedToolTips(); + if (d->m_tooltips.isEmpty()) + return; -void DebuggerToolTipManager::showToolTip(const DebuggerToolTipContext &context, - DebuggerEngine *engine) -{ - DebuggerToolTipWidget *tw = new DebuggerToolTipWidget; - tw->setContext(context); - tw->acquireEngine(engine); - const Utils::WidgetContent widgetContent(tw, true); - Utils::ToolTip::show(context.mousePosition, widgetContent, debuggerCore()->mainWindow()); - d->registerToolTip(tw); + // Stack frame changed: All tooltips of that file acquire the engine, + // all others release (arguable, this could be more precise?) + QString fileName; + QString function; + const int index = engine->stackHandler()->currentIndex(); + if (index >= 0) { + const StackFrame frame = engine->stackHandler()->currentFrame(); + if (frame.usable) { + fileName = frame.file; + function = frame.function; + } + } + foreach (const QPointer &tw, d->m_tooltips) + tw->handleStackFrameCompleted(fileName, function); + slotUpdateVisibleToolTips(); // Move out when stepping in same file. } -void DebuggerToolTipManagerData::registerToolTip(DebuggerToolTipWidget *toolTipWidget) +void DebuggerToolTipManager::deregisterEngine(DebuggerEngine *engine) { - QTC_ASSERT(toolTipWidget->context().isValid(), return); - m_tooltips.push_back(toolTipWidget); + QTC_ASSERT(engine, return); + foreach (const QPointer &tw, d->m_tooltips) + if (tw && tw->m_context.engineType == engine->objectName()) + tw->releaseEngine(); + saveSessionData(); } -void DebuggerToolTipManagerData::purgeClosedToolTips() +bool DebuggerToolTipManager::hasToolTips() { - for (DebuggerToolTipWidgetList::iterator it = m_tooltips.begin(); it != m_tooltips.end() ; ) { - if (it->isNull()) - it = m_tooltips.erase(it); - else - ++it; - } + return !d->m_tooltips.isEmpty(); } -void DebuggerToolTipManagerData::moveToolTipsBy(const QPoint &distance) +void DebuggerToolTipManager::showToolTip + (const DebuggerToolTipContext &context, DebuggerEngine *engine) { - purgeClosedToolTips(); - foreach (const QPointer &tw, m_tooltips) - if (tw->isVisible()) - tw->move(tw->pos() + distance); + QTC_ASSERT(engine, return); + QTC_ASSERT(!context.expression.isEmpty(), qDebug(" BUT EMPTY"); return); + + DebuggerToolTipWidget *tw = findOrCreateWidget(context); + tw->setWatchModel(engine->watchHandler()->model()); + tw->acquireEngine(); + + const Utils::WidgetContent widgetContent(tw, true); + Utils::ToolTip::show(context.mousePosition, widgetContent, debuggerCore()->mainWindow()); } bool DebuggerToolTipManager::eventFilter(QObject *o, QEvent *e) @@ -1241,8 +1153,12 @@ bool DebuggerToolTipManager::eventFilter(QObject *o, QEvent *e) switch (e->type()) { case QEvent::Move: { // Move along with parent (toplevel) const QMoveEvent *me = static_cast(e); - d->moveToolTipsBy(me->pos() - me->oldPos()); - } + const QPoint dist = me->pos() - me->oldPos(); + d->purgeClosedToolTips(); + foreach (const QPointer &tw, d->m_tooltips) + if (tw->isVisible()) + tw->move(tw->pos() + dist); + } break; case QEvent::WindowStateChange: { // Hide/Show along with parent (toplevel) const QWindowStateChangeEvent *se = static_cast(e); @@ -1266,59 +1182,35 @@ void DebuggerToolTipManager::sessionAboutToChange() closeAllToolTips(); } -static DebuggerToolTipWidget *loadSessionDataX(QXmlStreamReader &r) -{ - if (debugToolTips) - qDebug() << ">DebuggerToolTipWidget::loadSessionData" << r.tokenString() << r.name(); - DebuggerToolTipWidget *rc = loadSessionDataI(r); - if (debugToolTips) - qDebug() << "registerToolTip(tw); - - if (debugToolTips) - qDebug() << "DebuggerToolTipManager::loadSessionData version " << version << " restored " << d->m_tooltips.size(); - slotUpdateVisibleToolTips(); + if (r.tokenType() == QXmlStreamReader::StartElement && r.name() == QLatin1String(sessionDocumentC)) + while (!r.atEnd()) + loadSessionDataI(r); } void DebuggerToolTipManager::saveSessionData() { QString data; d->purgeClosedToolTips(); - if (!d->m_tooltips.isEmpty()) { - QXmlStreamWriter w(&data); - w.writeStartDocument(); - w.writeStartElement(QLatin1String(sessionDocumentC)); - w.writeAttribute(QLatin1String(sessionVersionAttributeC), QLatin1String("1.0")); - foreach (const QPointer &tw, d->m_tooltips) - if (tw->isPinned()) - tw->saveSessionData(w); - w.writeEndDocument(); - } - if (debugToolTips) - qDebug() << "DebuggerToolTipManager::saveSessionData" << d->m_tooltips.size() << data ; + + QXmlStreamWriter w(&data); + w.writeStartDocument(); + w.writeStartElement(QLatin1String(sessionDocumentC)); + w.writeAttribute(QLatin1String(sessionVersionAttributeC), QLatin1String("1.0")); + foreach (const QPointer &tw, d->m_tooltips) + if (tw->isPinned()) + tw->saveSessionData(w); + w.writeEndDocument(); + DebuggerCore::setSessionValue(sessionSettingsKeyC, QVariant(data)); } void DebuggerToolTipManager::closeAllToolTips() { - if (debugToolTips) - qDebug() << "DebuggerToolTipManager::closeAllToolTips"; - d->purgeClosedToolTips(); foreach (const QPointer &tw, d->m_tooltips) tw->close(); @@ -1342,9 +1234,6 @@ void DebuggerToolTipManager::slotUpdateVisibleToolTips() return; } - if (debugToolTips) - qDebug() << "DebuggerToolTipManager::slotUpdateVisibleToolTips() " << sender(); - DebuggerToolTipEditor toolTipEditor = DebuggerToolTipEditor(EditorManager::currentEditor()); if (!toolTipEditor.isValid() || toolTipEditor.fileName().isEmpty()) { hide(); @@ -1366,23 +1255,12 @@ void DebuggerToolTipManager::slotDebuggerStateChanged(DebuggerState state) const QObject *engine = sender(); QTC_ASSERT(engine, return); - const QString name = engine->objectName(); - if (debugToolTips) - qDebug() << "DebuggerToolTipWidget::debuggerStateChanged" - << engine << DebuggerEngine::stateName(state); - // Release at earliest possible convenience. switch (state) { - case InferiorRunRequested: - case DebuggerNotReady: case InferiorShutdownRequested: case EngineShutdownRequested: case DebuggerFinished: case EngineShutdownOk: { - d->purgeClosedToolTips(); - foreach (const QPointer &tw, d->m_tooltips) - if (tw->engineType() == name) - tw->releaseEngine(); break; } default: @@ -1390,54 +1268,6 @@ void DebuggerToolTipManager::slotDebuggerStateChanged(DebuggerState state) } } -void DebuggerToolTipManager::slotStackFrameCompleted() -{ - d->purgeClosedToolTips(); - if (d->m_tooltips.isEmpty()) - return; - DebuggerEngine *engine = qobject_cast(sender()); - QTC_ASSERT(engine, return); - - // Stack frame changed: All tooltips of that file acquire the engine, - // all others release (arguable, this could be more precise?) - QString fileName; - int lineNumber = 0; - // Get the current frame. - const QString engineName = engine->objectName(); - QString function; - const int index = engine->stackHandler()->currentIndex(); - if (index >= 0) { - const StackFrame frame = engine->stackHandler()->currentFrame(); - if (frame.usable) { - fileName = frame.file; - lineNumber = frame.line; - function = frame.function; - } - } - if (debugToolTips) - qDebug("DebuggerToolTipWidget::slotStackFrameCompleted(%s, %s@%d, %s())", - qPrintable(engineName), qPrintable(fileName), lineNumber, - qPrintable(function)); - unsigned acquiredCount = 0; - foreach (const QPointer &tw, d->m_tooltips) { - if (tw->matches(fileName, engineName, function)) { - tw->acquireEngine(engine); - acquiredCount++; - } else { - tw->releaseEngine(); - } - } - slotUpdateVisibleToolTips(); // Move out when stepping in same file. - if (debugToolTips) - qDebug() << "DebuggerToolTipWidget::slotStackFrameCompleted()" - << engineName << fileName << lineNumber << " acquired " << acquiredCount; -} - -bool DebuggerToolTipManager::debug() -{ - return debugToolTips; -} - void DebuggerToolTipManager::slotEditorOpened(IEditor *e) { // Move tooltip along when scrolled. @@ -1453,9 +1283,6 @@ void DebuggerToolTipManager::slotEditorOpened(IEditor *e) void DebuggerToolTipManager::debugModeEntered() { - if (debugToolTips) - qDebug("DebuggerToolTipManager::debugModeEntered"); - // Hook up all signals in debug mode. if (!d->m_debugModeActive) { d->m_debugModeActive = true; @@ -1476,9 +1303,6 @@ void DebuggerToolTipManager::debugModeEntered() void DebuggerToolTipManager::leavingDebugMode() { - if (debugToolTips) - qDebug("DebuggerToolTipManager::leavingDebugMode"); - // Remove all signals in debug mode. if (d->m_debugModeActive) { d->m_debugModeActive = false; @@ -1498,68 +1322,81 @@ void DebuggerToolTipManager::leavingDebugMode() } } -void DebuggerToolTipManager::slotTooltipOverrideRequested(ITextEditor *editor, - const QPoint &point, - int pos, bool *handled) +void DebuggerToolTipManager::slotTooltipOverrideRequested + (ITextEditor *editor, const QPoint &point, int pos, bool *handled) { QTC_ASSERT(handled, return); + QTC_ASSERT(editor, return); + QTC_ASSERT(editor->document(), return); const int movedDistance = (point - d->m_lastToolTipPoint).manhattanLength(); - const bool samePosition = d->m_lastToolTipEditor == editor && movedDistance < 25; - if (debugToolTipPositioning) - qDebug() << ">slotTooltipOverrideRequested() " << editor << point - << "from " << d->m_lastToolTipPoint << ") pos: " - << pos << *handled - << " Same position=" << samePosition << " d=" << movedDistance; - - DebuggerEngine *currentEngine = 0; - do { - if (samePosition) - *handled = true; - - if (*handled) - break; // Avoid flicker. - - DebuggerCore *core = debuggerCore(); - if (!core->boolSetting(UseToolTipsInMainEditor)) - break; + if (d->m_lastToolTipEditor == editor && movedDistance < 25) { + *handled = true; + return; + } - currentEngine = core->currentEngine(); - if (!currentEngine || !currentEngine->canDisplayTooltip()) - break; + *handled = tryHandleToolTipOverride(editor, point, pos); - DebuggerToolTipContext context = DebuggerToolTipContext::fromEditor(editor, pos); - context.mousePosition = point; - if (context.isValid() && currentEngine->setToolTipExpression(editor, context)) { - *handled = true; - d->m_lastToolTipEditor = editor; - d->m_lastToolTipPoint = point; - } + if (*handled) { + d->m_lastToolTipEditor = editor; + d->m_lastToolTipPoint = point; + } else { + d->m_lastToolTipEditor = 0; + d->m_lastToolTipPoint = QPoint(-1, -1); + } +} - } while (false); +bool DebuggerToolTipManager::tryHandleToolTipOverride(ITextEditor *editor, const QPoint &point, int pos) +{ + DebuggerCore *core = debuggerCore(); + if (!core->boolSetting(UseToolTipsInMainEditor)) + return false; + + DebuggerEngine *currentEngine = core->currentEngine(); + if (!currentEngine || !currentEngine->canDisplayTooltip()) + return false; + + DebuggerToolTipContext context; + context.engineType = currentEngine->objectName(); + context.fileName = editor->document()->filePath(); + context.position = pos; + context.mousePosition = point; + editor->convertPosition(pos, &context.line, &context.column); + QString raw = cppExpressionAt(editor, context.position, &context.line, &context.column, &context.function); + context.expression = fixCppExpression(raw); + + if (context.expression.isEmpty()) + return false; + + // Prefer a filter on an existing local variable if it can be found. + if (const WatchData *localVariable = currentEngine->watchHandler()->findCppLocalVariable(context.expression)) { + context.expression = QLatin1String(localVariable->exp); + if (context.expression.isEmpty()) + context.expression = localVariable->name; + context.iname = localVariable->iname; + showToolTip(context, currentEngine); + return true; + } + + context.iname = "tooltip." + context.expression.toLatin1().toHex(); + + if (currentEngine->setToolTipExpression(editor, context)) + return true; // Other tooltip, close all in case mouse never entered the tooltip // and no leave was triggered. - if (!*handled) { - d->m_lastToolTipEditor = 0; - d->m_lastToolTipPoint = QPoint(-1, -1); - } - if (debugToolTipPositioning) - qDebug() << " &tw, d->m_tooltips) { - if (!tw.isNull() && tw->matches(fileName, engineType, function)) + if (tw && tw->context().matchesFrame(fileName, function)) rc.push_back(tw->context()); } - if (debugToolTips) - qDebug() << "DebuggerToolTipManager::treeWidgetExpressions" - << fileName << engineType << function << rc; return rc; } diff --git a/src/plugins/debugger/debuggertooltipmanager.h b/src/plugins/debugger/debuggertooltipmanager.h index fb45e6536c..2957c9e633 100644 --- a/src/plugins/debugger/debuggertooltipmanager.h +++ b/src/plugins/debugger/debuggertooltipmanager.h @@ -52,14 +52,17 @@ class DebuggerToolTipContext { public: DebuggerToolTipContext(); - static DebuggerToolTipContext fromEditor(Core::IEditor *ed, int pos); - bool isValid() const { return !fileName.isEmpty(); } + bool isValid() const { return !expression.isEmpty(); } + bool matchesFrame(const QString &frameFile, const QString &frameFunction) const; + bool isSame(const DebuggerToolTipContext &other) const; QString fileName; int position; int line; int column; QString function; //!< Optional function. This must be set by the engine as it is language-specific. + QString engineType; + QDate creationDate; QPoint mousePosition; QString expression; @@ -85,7 +88,6 @@ private slots: void computeSize(); void expandNode(const QModelIndex &idx); void collapseNode(const QModelIndex &idx); - void handleItemIsExpanded(const QModelIndex &sourceIdx); private: int computeHeight(const QModelIndex &index) const; @@ -102,37 +104,40 @@ public: ~DebuggerToolTipManager(); static void registerEngine(DebuggerEngine *engine); + static void deregisterEngine(DebuggerEngine *engine); + static void updateEngine(DebuggerEngine *engine); static bool hasToolTips(); // Collect all expressions of DebuggerTreeViewToolTipWidget - static DebuggerToolTipContexts treeWidgetExpressions(const QString &fileName, - const QString &engineType = QString(), - const QString &function= QString()); + static DebuggerToolTipContexts treeWidgetExpressions(DebuggerEngine *engine, + const QString &fileName, const QString &function = QString()); static void showToolTip(const DebuggerToolTipContext &context, DebuggerEngine *engine); virtual bool eventFilter(QObject *, QEvent *); - static bool debug(); - static QString treeModelClipboardContents(const QAbstractItemModel *m); + static QString treeModelClipboardContents(const QAbstractItemModel *model); public slots: void debugModeEntered(); void leavingDebugMode(); void sessionAboutToChange(); - void loadSessionData(); - void saveSessionData(); + static void loadSessionData(); + static void saveSessionData(); static void closeAllToolTips(); - void hide(); + static void hide(); private slots: - void slotUpdateVisibleToolTips(); + static void slotUpdateVisibleToolTips(); void slotDebuggerStateChanged(Debugger::DebuggerState); - void slotStackFrameCompleted(); void slotEditorOpened(Core::IEditor *); void slotTooltipOverrideRequested(TextEditor::ITextEditor *editor, const QPoint &point, int pos, bool *handled); + +private: + bool tryHandleToolTipOverride(TextEditor::ITextEditor *editor, + const QPoint &point, int pos); }; } // namespace Internal diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index b98b2354f8..e635cbbd45 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3629,39 +3629,30 @@ void GdbEngine::handleRegisterListValues(const GdbResponse &response) // ////////////////////////////////////////////////////////////////////// -void GdbEngine::showToolTip() -{ - if (m_toolTipContext.isNull()) - return; - const QString expression = m_toolTipContext->expression; - if (DebuggerToolTipManager::debug()) - qDebug() << "GdbEngine::showToolTip " << expression << m_toolTipContext->iname << (*m_toolTipContext); - - if (m_toolTipContext->iname.startsWith("tooltip") - && (!debuggerCore()->boolSetting(UseToolTipsInMainEditor) - || !watchHandler()->isValidToolTip(m_toolTipContext->iname))) { - watchHandler()->removeData(m_toolTipContext->iname); - return; - } - - DebuggerToolTipManager::showToolTip(*m_toolTipContext, this); - // Prevent tooltip from re-occurring (classic GDB, QTCREATORBUG-4711). - m_toolTipContext.reset(); -} - -QString GdbEngine::tooltipExpression() const -{ - return m_toolTipContext.isNull() ? QString() : m_toolTipContext->expression; -} +//void GdbEngine::showToolTip() +//{ +// const QString expression = m_toolTipContext.expression; +// if (DebuggerToolTipManager::debug()) +// qDebug() << "GdbEngine::showToolTip " << expression << m_toolTipContext.iname << m_toolTipContext; + +// if (m_toolTipContext.iname.startsWith("tooltip") +// && (!debuggerCore()->boolSetting(UseToolTipsInMainEditor) +// || !watchHandler()->isValidToolTip(m_toolTipContext.iname))) { +// watchHandler()->removeData(m_toolTipContext.iname); +// return; +// } + +// DebuggerToolTipManager::showToolTip(m_toolTipContext, this); +//} void GdbEngine::resetLocation() { - m_toolTipContext.reset(); + m_toolTipContext.expression.clear(); DebuggerEngine::resetLocation(); } bool GdbEngine::setToolTipExpression(TextEditor::ITextEditor *editor, - const DebuggerToolTipContext &contextIn) + const DebuggerToolTipContext &context) { if (state() != InferiorStopOk || !isCppEditor(editor)) { //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED " @@ -3669,45 +3660,13 @@ bool GdbEngine::setToolTipExpression(TextEditor::ITextEditor *editor, return false; } - DebuggerToolTipContext context = contextIn; - int line, column; - QString exp = fixCppExpression(cppExpressionAt(editor, context.position, &line, &column, &context.function)); - if (exp.isEmpty()) - return false; - // Prefer a filter on an existing local variable if it can be found. - QByteArray iname; - if (const WatchData *localVariable = watchHandler()->findCppLocalVariable(exp)) { - exp = QLatin1String(localVariable->exp); - iname = localVariable->iname; - } else { - iname = tooltipIName(exp); - } - - if (DebuggerToolTipManager::debug()) - qDebug() << "GdbEngine::setToolTipExpression1 " << exp << iname << context; - - // Same expression: Display synchronously. - if (!m_toolTipContext.isNull() && m_toolTipContext->expression == exp) { - showToolTip(); - return true; - } - - m_toolTipContext.reset(new DebuggerToolTipContext(context)); - m_toolTipContext->expression = exp; - m_toolTipContext->iname = iname; - // Local variable: Display synchronously. - if (iname.startsWith("local")) { - showToolTip(); - return true; - } - - if (DebuggerToolTipManager::debug()) - qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext); + m_toolTipContext = context; + // qDebug() << "GdbEngine::setToolTipExpression2 " << exp << m_toolTipContext; UpdateParameters params; params.tryPartial = true; params.tooltipOnly = true; - params.varList = iname; + params.varList = context.iname; updateLocalsPython(params); return true; } @@ -3773,7 +3732,12 @@ void GdbEngine::rebuildWatchModel() showMessage(LogWindow::logTimeStamp(), LogMiscInput); showMessage(_("").arg(count), LogMiscInput); showStatusMessage(tr("Finished retrieving data"), 400); - showToolTip(); + + if (m_toolTipContext.isValid()) { + DebuggerToolTipManager::showToolTip(m_toolTipContext, this); + m_toolTipContext = DebuggerToolTipContext(); + } + DebuggerToolTipManager::updateEngine(this); } void GdbEngine::handleVarAssign(const GdbResponse &) @@ -4875,9 +4839,9 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) // Re-create tooltip items that are not filters on existing local variables in // the tooltip model. DebuggerToolTipContexts toolTips = - DebuggerToolTipManager::treeWidgetExpressions(fileName, objectName(), function); + DebuggerToolTipManager::treeWidgetExpressions(this, fileName, function); - const QString currentExpression = tooltipExpression(); + const QString currentExpression = m_toolTipContext.expression; if (!currentExpression.isEmpty()) { int currentIndex = -1; for (int i = 0; i < toolTips.size(); ++i) { @@ -5015,8 +4979,10 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response) //PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n"); rebuildWatchModel(); //} - if (!partial) + if (!partial) { emit stackFrameCompleted(); + DebuggerToolTipManager::updateEngine(this); + } } else { showMessage(_("DUMPER FAILED: " + response.toString())); } diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 31386ad45f..b30e40d5bf 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -34,6 +34,7 @@ #include #include +#include #include @@ -467,8 +468,7 @@ protected: void showExecutionError(const QString &message); static QByteArray tooltipIName(const QString &exp); - QString tooltipExpression() const; - QScopedPointer m_toolTipContext; + DebuggerToolTipContext m_toolTipContext; // For short-circuiting stack and thread list evaluation. bool m_stackNeeded; diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index d83be06da8..a66db55f42 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -821,31 +821,23 @@ static QHash m_toolTipCache; void LldbEngine::showToolTip() { - if (m_toolTipContext.isNull()) + if (m_toolTipContext.expression.isEmpty()) return; - const QString expression = m_toolTipContext->expression; - if (DebuggerToolTipManager::debug()) - qDebug() << "LldbEngine::showToolTip " << expression << m_toolTipContext->iname << (*m_toolTipContext); - - if (m_toolTipContext->iname.startsWith("tooltip") - && (!debuggerCore()->boolSetting(UseToolTipsInMainEditor) - || !watchHandler()->isValidToolTip(m_toolTipContext->iname))) { - watchHandler()->removeData(m_toolTipContext->iname); - return; - } + //const QString expression = m_toolTipContext->expression; + // qDebug() << "LldbEngine::showToolTip " << expression << m_toolTipContext->iname << (*m_toolTipContext); - DebuggerToolTipManager::showToolTip(*m_toolTipContext, this); + DebuggerToolTipManager::showToolTip(m_toolTipContext, this); // Prevent tooltip from re-occurring (classic GDB, QTCREATORBUG-4711). - m_toolTipContext.reset(); + m_toolTipContext.expression.clear(); } void LldbEngine::resetLocation() { - m_toolTipContext.reset(); + m_toolTipContext.expression.clear(); DebuggerEngine::resetLocation(); } -bool LldbEngine::setToolTipExpression(TextEditor::ITextEditor *editor, const DebuggerToolTipContext &contextIn) +bool LldbEngine::setToolTipExpression(TextEditor::ITextEditor *editor, const DebuggerToolTipContext &context) { if (state() != InferiorStopOk || !isCppEditor(editor)) { //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED " @@ -853,45 +845,12 @@ bool LldbEngine::setToolTipExpression(TextEditor::ITextEditor *editor, const Deb return false; } - DebuggerToolTipContext context = contextIn; - int line, column; - QString exp = fixCppExpression(cppExpressionAt(editor, context.position, &line, &column, &context.function)); - if (exp.isEmpty()) - return false; - // Prefer a filter on an existing local variable if it can be found. - QByteArray iname; - if (const WatchData *localVariable = watchHandler()->findCppLocalVariable(exp)) { - exp = QLatin1String(localVariable->exp); - iname = localVariable->iname; - } else { - iname = tooltipIName(exp); - } - - if (DebuggerToolTipManager::debug()) - qDebug() << "GdbEngine::setToolTipExpression1 " << exp << iname << context; - - // Same expression: Display synchronously. - if (!m_toolTipContext.isNull() && m_toolTipContext->expression == exp) { - showToolTip(); - return true; - } - - m_toolTipContext.reset(new DebuggerToolTipContext(context)); - m_toolTipContext->expression = exp; - m_toolTipContext->iname = iname; - // Local variable: Display synchronously. - if (iname.startsWith("local")) { - showToolTip(); - return true; - } - - if (DebuggerToolTipManager::debug()) - qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext); + m_toolTipContext = context; UpdateParameters params; params.tryPartial = true; params.tooltipOnly = true; - params.varList = iname; + params.varList = context.iname; doUpdateLocals(params); return true; @@ -980,10 +939,9 @@ void LldbEngine::doUpdateLocals(UpdateParameters params) // Re-create tooltip items that are not filters on existing local variables in // the tooltip model. DebuggerToolTipContexts toolTips = - DebuggerToolTipManager::treeWidgetExpressions(frame.file, objectName(), frame.function); + DebuggerToolTipManager::treeWidgetExpressions(this, frame.file, frame.function); - const QString currentExpression = - m_toolTipContext.isNull() ? QString() : m_toolTipContext->expression; + const QString currentExpression = m_toolTipContext.expression; if (!currentExpression.isEmpty()) { int currentIndex = -1; for (int i = 0; i < toolTips.size(); ++i) { diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index aaa9ecdc1e..38ff85ba82 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -217,7 +218,7 @@ private: QMap, int> m_disassemblerAgents; QMap, int> m_memoryAgents; QHash > m_memoryAgentTokens; - QScopedPointer m_toolTipContext; + DebuggerToolTipContext m_toolTipContext; void showToolTip(); diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 8c45d960f2..ef16e42bd8 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -500,7 +500,11 @@ WatchItem *WatchModel::findItem(const QByteArray &iname) const void WatchModel::checkIndex(const QModelIndex &index) const { - QTC_CHECK(index.isValid() ? index.model() == this : index.model() == 0); + if (index.isValid()) { + QTC_CHECK(index.model() == this); + } else { + QTC_CHECK(index.model() == 0); + } } WatchItem *WatchModel::createItem(const WatchData &data) diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index fc80359f09..fbaffb1237 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -1025,31 +1025,31 @@ void WatchTreeView::handleItemIsExpanded(const QModelIndex &idx) expand(idx); } -void WatchTreeView::resetHelper() -{ - QModelIndex idx = model()->index(m_type, 0); - resetHelper(idx); - expand(idx); -} - -void WatchTreeView::resetHelper(const QModelIndex &idx) +void WatchTreeView::reexpand(QTreeView *view, const QModelIndex &idx) { if (idx.data(LocalsExpandedRole).toBool()) { - //qDebug() << "EXPANDING " << model()->data(idx, LocalsINameRole); - if (!isExpanded(idx)) { - expand(idx); - for (int i = 0, n = model()->rowCount(idx); i != n; ++i) { - QModelIndex idx1 = model()->index(i, 0, idx); - resetHelper(idx1); + //qDebug() << "EXPANDING " << view->model()->data(idx, LocalsINameRole); + if (!view->isExpanded(idx)) { + view->expand(idx); + for (int i = 0, n = view->model()->rowCount(idx); i != n; ++i) { + QModelIndex idx1 = view->model()->index(i, 0, idx); + reexpand(view, idx1); } } } else { - //qDebug() << "COLLAPSING " << model()->data(idx, LocalsINameRole); - if (isExpanded(idx)) - collapse(idx); + //qDebug() << "COLLAPSING " << view->model()->data(idx, LocalsINameRole); + if (view->isExpanded(idx)) + view->collapse(idx); } } +void WatchTreeView::resetHelper() +{ + QModelIndex idx = model()->index(m_type, 0); + reexpand(this, idx); + expand(idx); +} + void WatchTreeView::reset() { BaseTreeView::reset(); diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h index caeb68bef4..ff03e6aaf0 100644 --- a/src/plugins/debugger/watchwindow.h +++ b/src/plugins/debugger/watchwindow.h @@ -56,6 +56,7 @@ public: void reset(); void fillFormatMenu(QMenu *, const QModelIndex &mi); + static void reexpand(QTreeView *view, const QModelIndex &idx); public slots: void watchExpression(const QString &exp); @@ -91,7 +92,6 @@ private: void inputNewExpression(); void editItem(const QModelIndex &idx); - void resetHelper(const QModelIndex &idx); void setModelData(int role, const QVariant &value = QVariant(), const QModelIndex &index = QModelIndex()); -- GitLab