diff --git a/language/duchain/duchainutils.cpp b/language/duchain/duchainutils.cpp index 40303f815e..83f8d71646 100644 --- a/language/duchain/duchainutils.cpp +++ b/language/duchain/duchainutils.cpp @@ -1,623 +1,627 @@ /* * DUChain Utilities * * Copyright 2007 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "duchainutils.h" #include #include #include #include "../interfaces/ilanguagesupport.h" #include "../assistant/staticassistantsmanager.h" #include "util/debug.h" #include "declaration.h" #include "classfunctiondeclaration.h" #include "ducontext.h" #include "duchain.h" #include "use.h" #include "duchainlock.h" #include "classmemberdeclaration.h" #include "functiondefinition.h" #include "specializationstore.h" #include "persistentsymboltable.h" #include "classdeclaration.h" #include "parsingenvironment.h" #include using namespace KDevelop; using namespace KTextEditor; CodeCompletionModel::CompletionProperties DUChainUtils::completionProperties(const Declaration* dec) { CodeCompletionModel::CompletionProperties p; if(dec->context()->type() == DUContext::Class) { if (const ClassMemberDeclaration* member = dynamic_cast(dec)) { switch (member->accessPolicy()) { case Declaration::Public: p |= CodeCompletionModel::Public; break; case Declaration::Protected: p |= CodeCompletionModel::Protected; break; case Declaration::Private: p |= CodeCompletionModel::Private; break; default: break; } if (member->isStatic()) p |= CodeCompletionModel::Static; if (member->isAuto()) {}//TODO if (member->isFriend()) p |= CodeCompletionModel::Friend; if (member->isRegister()) {}//TODO if (member->isExtern()) {}//TODO if (member->isMutable()) {}//TODO } } if (const AbstractFunctionDeclaration* function = dynamic_cast(dec)) { p |= CodeCompletionModel::Function; if (function->isVirtual()) p |= CodeCompletionModel::Virtual; if (function->isInline()) p |= CodeCompletionModel::Inline; if (function->isExplicit()) {}//TODO } if( dec->isTypeAlias() ) p |= CodeCompletionModel::TypeAlias; if (dec->abstractType()) { switch (dec->abstractType()->whichType()) { case AbstractType::TypeIntegral: p |= CodeCompletionModel::Variable; break; case AbstractType::TypePointer: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeReference: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeFunction: p |= CodeCompletionModel::Function; break; case AbstractType::TypeStructure: p |= CodeCompletionModel::Class; break; case AbstractType::TypeArray: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeEnumeration: p |= CodeCompletionModel::Enum; break; case AbstractType::TypeEnumerator: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeAbstract: case AbstractType::TypeDelayed: case AbstractType::TypeUnsure: case AbstractType::TypeAlias: // TODO break; } if( dec->abstractType()->modifiers() & AbstractType::ConstModifier ) p |= CodeCompletionModel::Const; if( dec->kind() == Declaration::Instance && !dec->isFunctionDeclaration() ) p |= CodeCompletionModel::Variable; } if (dec->context()) { if( dec->context()->type() == DUContext::Global ) p |= CodeCompletionModel::GlobalScope; else if( dec->context()->type() == DUContext::Namespace ) p |= CodeCompletionModel::NamespaceScope; else if( dec->context()->type() != DUContext::Class && dec->context()->type() != DUContext::Enum ) p |= CodeCompletionModel::LocalScope; } return p; } /**We have to construct the item from the pixmap, else the icon will be marked as "load on demand", * and for some reason will be loaded every time it's used(this function returns a QIcon marked "load on demand" * each time this is called). And the loading is very slow. Seems like a bug somewhere, it cannot be ment to be that slow. */ #define RETURN_CACHED_ICON(name) {static QIcon icon(QIcon( \ QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevelop/pics/" name ".png"))\ ).pixmap(QSize(16, 16)));\ return icon;} QIcon DUChainUtils::iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p) { if( (p & CodeCompletionModel::Variable) ) if( (p & CodeCompletionModel::Protected) ) RETURN_CACHED_ICON("CVprotected_var") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("CVprivate_var") else RETURN_CACHED_ICON("CVpublic_var") else if( (p & CodeCompletionModel::Union) && (p & CodeCompletionModel::Protected) ) RETURN_CACHED_ICON("protected_union") else if( p & CodeCompletionModel::Enum ) if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("protected_enum") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_enum") else RETURN_CACHED_ICON("enum") else if( p & CodeCompletionModel::Struct ) if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_struct") else RETURN_CACHED_ICON("struct") else if( p & CodeCompletionModel::Slot ) if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("CVprotected_slot") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("CVprivate_slot") else if(p & CodeCompletionModel::Public ) RETURN_CACHED_ICON("CVpublic_slot") else RETURN_CACHED_ICON("slot") else if( p & CodeCompletionModel::Signal ) if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("CVprotected_signal") else RETURN_CACHED_ICON("signal") else if( p & CodeCompletionModel::Class ) if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Protected) ) RETURN_CACHED_ICON("protected_class") else if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Private) ) RETURN_CACHED_ICON("private_class") else RETURN_CACHED_ICON("code-class") else if( p & CodeCompletionModel::Union ) if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_union") else RETURN_CACHED_ICON("union") else if( p & CodeCompletionModel::TypeAlias ) if ((p & CodeCompletionModel::Const) /*|| (p & CodeCompletionModel::Volatile)*/) RETURN_CACHED_ICON("CVtypedef") else RETURN_CACHED_ICON("typedef") else if( p & CodeCompletionModel::Function ) { if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("protected_function") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_function") else RETURN_CACHED_ICON("code-function") } if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("protected_field") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_field") else RETURN_CACHED_ICON("field") return QIcon(); } QIcon DUChainUtils::iconForDeclaration(const Declaration* dec) { return iconForProperties(completionProperties(dec)); } TopDUContext* DUChainUtils::contentContextFromProxyContext(TopDUContext* top) { if(!top) return 0; if(top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->isProxyContext()) { if(!top->importedParentContexts().isEmpty()) { DUContext* ctx = top->importedParentContexts().at(0).context(0); if(!ctx) return 0; TopDUContext* ret = ctx->topContext(); if(!ret) return 0; if(ret->url() != top->url()) qCDebug(LANGUAGE) << "url-mismatch between content and proxy:" << top->url().toUrl() << ret->url().toUrl(); if(ret->url() == top->url() && !ret->parsingEnvironmentFile()->isProxyContext()) return ret; } else { qCDebug(LANGUAGE) << "Proxy-context imports no content-context"; } } else return top; return 0; } TopDUContext* DUChainUtils::standardContextForUrl(const QUrl& url, bool preferProxyContext) { KDevelop::TopDUContext* chosen = 0; auto languages = ICore::self()->languageController()->languagesForUrl(url); foreach(const auto language, languages) { if(!chosen) { chosen = language->standardContext(url, preferProxyContext); } } if(!chosen) chosen = DUChain::self()->chainForDocument(IndexedString(url), preferProxyContext); if(!chosen && preferProxyContext) return standardContextForUrl(url, false); // Fall back to a normal context return chosen; } struct ItemUnderCursorInternal { Declaration* declaration; DUContext* context; RangeInRevision range; }; ItemUnderCursorInternal itemUnderCursorInternal(const CursorInRevision& c, DUContext* ctx, RangeInRevision::ContainsBehavior behavior) { //Search all collapsed sub-contexts. In C++, those can contain declarations that have ranges out of the context foreach(DUContext* subCtx, ctx->childContexts()) { //This is a little hacky, but we need it in case of foreach macros and similar stuff if(subCtx->range().contains(c, behavior) || subCtx->range().isEmpty() || subCtx->range().start.line == c.line || subCtx->range().end.line == c.line) { ItemUnderCursorInternal sub = itemUnderCursorInternal(c, subCtx, behavior); if(sub.declaration) { return sub; } } } foreach(Declaration* decl, ctx->localDeclarations()) { if(decl->range().contains(c, behavior)) { return {decl, ctx, decl->range()}; } } //Try finding a use under the cursor for(int a = 0; a < ctx->usesCount(); ++a) { if(ctx->uses()[a].m_range.contains(c, behavior)) { return {ctx->topContext()->usedDeclarationForIndex(ctx->uses()[a].m_declarationIndex), ctx, ctx->uses()[a].m_range}; } } return {nullptr, nullptr, RangeInRevision()}; } DUChainUtils::ItemUnderCursor DUChainUtils::itemUnderCursor(const QUrl& url, const KTextEditor::Cursor& cursor) { KDevelop::TopDUContext* top = standardContextForUrl(url); if(!top) { return {nullptr, nullptr, KTextEditor::Range()}; } - ItemUnderCursorInternal decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::IncludeBackEdge); + ItemUnderCursorInternal decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::Default); + if (decl.declaration == nullptr) + { + decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::IncludeBackEdge); + } return {decl.declaration, decl.context, top->transformFromLocalRevision(decl.range)}; } Declaration* DUChainUtils::declarationForDefinition(Declaration* definition, TopDUContext* topContext) { if(!definition) return 0; if(!topContext) topContext = definition->topContext(); if(dynamic_cast(definition)) { Declaration* ret = static_cast(definition)->declaration(); if(ret) return ret; } return definition; } Declaration* DUChainUtils::declarationInLine(const KTextEditor::Cursor& _cursor, DUContext* ctx) { if(!ctx) return 0; CursorInRevision cursor = ctx->transformToLocalRevision(_cursor); foreach(Declaration* decl, ctx->localDeclarations()) { if(decl->range().start.line == cursor.line) return decl; DUContext* funCtx = getFunctionContext(decl); if(funCtx && funCtx->range().contains(cursor)) return decl; } foreach(DUContext* child, ctx->childContexts()){ Declaration* decl = declarationInLine(_cursor, child); if(decl) return decl; } return 0; } DUChainUtils::DUChainItemFilter::~DUChainItemFilter() { } void DUChainUtils::collectItems( DUContext* context, DUChainItemFilter& filter ) { QVector children = context->childContexts(); QVector localDeclarations = context->localDeclarations(); QVector::const_iterator childIt = children.constBegin(); QVector::const_iterator declIt = localDeclarations.constBegin(); while(childIt != children.constEnd() || declIt != localDeclarations.constEnd()) { DUContext* child = 0; if(childIt != children.constEnd()) child = *childIt; Declaration* decl = 0; if(declIt != localDeclarations.constEnd()) decl = *declIt; if(decl) { if(child && child->range().start.line >= decl->range().start.line) child = 0; } if(child) { if(decl && decl->range().start >= child->range().start) decl = 0; } if(decl) { if( filter.accept(decl) ) { //Action is done in the filter } ++declIt; continue; } if(child) { if( filter.accept(child) ) collectItems(child, filter); ++childIt; continue; } } } KDevelop::DUContext* DUChainUtils::getArgumentContext(KDevelop::Declaration* decl) { DUContext* internal = decl->internalContext(); if( !internal ) return 0; if( internal->type() == DUContext::Function ) return internal; foreach( const DUContext::Import &ctx, internal->importedParentContexts() ) { if( ctx.context(decl->topContext()) ) if( ctx.context(decl->topContext())->type() == DUContext::Function ) return ctx.context(decl->topContext()); } return 0; } QList DUChainUtils::collectAllVersions(Declaration* decl) { QList ret; ret << IndexedDeclaration(decl); if(decl->inSymbolTable()) { uint count; const IndexedDeclaration* allDeclarations; PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations); for(uint a = 0; a < count; ++a) if(!(allDeclarations[a] == IndexedDeclaration(decl))) ret << allDeclarations[a]; } return ret; } static QList getInheritersInternal(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions) { QList ret; if(!dynamic_cast(decl)) return ret; if(maxAllowedSteps == 0) return ret; if(decl->internalContext() && decl->internalContext()->type() == DUContext::Class) { foreach (const IndexedDUContext importer, decl->internalContext()->indexedImporters()) { DUContext* imp = importer.data(); if(!imp) continue; if(imp->type() == DUContext::Class && imp->owner()) ret << imp->owner(); --maxAllowedSteps; if(maxAllowedSteps == 0) return ret; } } if(collectVersions && decl->inSymbolTable()) { uint count; const IndexedDeclaration* allDeclarations; PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations); for(uint a = 0; a < count; ++a) { ++maxAllowedSteps; if(allDeclarations[a].data() && allDeclarations[a].data() != decl) { ret += getInheritersInternal(allDeclarations[a].data(), maxAllowedSteps, false); } if(maxAllowedSteps == 0) return ret; } } return ret; } QList DUChainUtils::getInheriters(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions) { auto inheriters = getInheritersInternal(decl, maxAllowedSteps, collectVersions); // remove duplicates std::sort(inheriters.begin(), inheriters.end()); inheriters.erase(std::unique(inheriters.begin(), inheriters.end()), inheriters.end()); return inheriters; } QList DUChainUtils::getOverriders(const Declaration* currentClass, const Declaration* overriddenDeclaration, uint& maxAllowedSteps) { QList ret; if(maxAllowedSteps == 0) return ret; if(currentClass != overriddenDeclaration->context()->owner() && currentClass->internalContext()) ret += currentClass->internalContext()->findLocalDeclarations(overriddenDeclaration->identifier(), CursorInRevision::invalid(), currentClass->topContext(), overriddenDeclaration->abstractType()); foreach(Declaration* inheriter, getInheriters(currentClass, maxAllowedSteps)) ret += getOverriders(inheriter, overriddenDeclaration, maxAllowedSteps); return ret; } static bool hasUse(DUContext* context, int usedDeclarationIndex) { if(usedDeclarationIndex == std::numeric_limits::max()) return false; for(int a = 0; a < context->usesCount(); ++a) if(context->uses()[a].m_declarationIndex == usedDeclarationIndex) return true; foreach(DUContext* child, context->childContexts()) if(hasUse(child, usedDeclarationIndex)) return true; return false; } bool DUChainUtils::contextHasUse(DUContext* context, Declaration* declaration) { return hasUse(context, context->topContext()->indexForUsedDeclaration(declaration, false)); } static uint countUses(DUContext* context, int usedDeclarationIndex) { if(usedDeclarationIndex == std::numeric_limits::max()) return 0; uint ret = 0; for(int a = 0; a < context->usesCount(); ++a) if(context->uses()[a].m_declarationIndex == usedDeclarationIndex) ++ret; foreach(DUContext* child, context->childContexts()) ret += countUses(child, usedDeclarationIndex); return ret; } uint DUChainUtils::contextCountUses(DUContext* context, Declaration* declaration) { return countUses(context, context->topContext()->indexForUsedDeclaration(declaration, false)); } Declaration* DUChainUtils::getOverridden(const Declaration* decl) { const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl); if(!classFunDecl || !classFunDecl->isVirtual()) return 0; QList decls; foreach(const DUContext::Import &import, decl->context()->importedParentContexts()) { DUContext* ctx = import.context(decl->topContext()); if(ctx) decls += ctx->findDeclarations(QualifiedIdentifier(decl->identifier()), CursorInRevision::invalid(), decl->abstractType(), decl->topContext(), DUContext::DontSearchInParent); } foreach(Declaration* found, decls) { const ClassFunctionDeclaration* foundClassFunDecl = dynamic_cast(found); if(foundClassFunDecl && foundClassFunDecl->isVirtual()) return found; } return 0; } DUContext* DUChainUtils::getFunctionContext(Declaration* decl) { DUContext* functionContext = decl->internalContext(); if(functionContext && functionContext->type() != DUContext::Function) { foreach(const DUContext::Import& import, functionContext->importedParentContexts()) { DUContext* ctx = import.context(decl->topContext()); if(ctx && ctx->type() == DUContext::Function) functionContext = ctx; } } if(functionContext && functionContext->type() == DUContext::Function) return functionContext; return 0; } QVector KDevelop::DUChainUtils::allProblemsForContext(KDevelop::ReferencedTopDUContext top) { QVector ret; Q_FOREACH ( const auto& p, top->problems() ) { ret << p; } Q_FOREACH ( const auto& p, ICore::self()->languageController()->staticAssistantsManager()->problemsForContext(top) ) { ret << p; } return ret; } diff --git a/language/duchain/navigation/abstractnavigationwidget.h b/language/duchain/navigation/abstractnavigationwidget.h index d3e82d1402..9dfd767a54 100644 --- a/language/duchain/navigation/abstractnavigationwidget.h +++ b/language/duchain/navigation/abstractnavigationwidget.h @@ -1,113 +1,113 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H #define KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H #include #include #include #include #include "../../interfaces/quickopendataprovider.h" #include #include "abstractnavigationcontext.h" class QTextBrowser; namespace KDevelop { /** * This class deleted itself when its part is deleted, so always use a QPointer when referencing it. * The duchain must be read-locked for most operations * */ class KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationWidget : public QWidget, public QuickOpenEmbeddedWidgetInterface { Q_OBJECT public: enum DisplayHint { NoHints = 0x0, // < Normal display EmbeddableWidget = 0x1, // < Omit parts which are only useful for the navigation popup }; - Q_DECLARE_FLAGS(DisplayHints, DisplayHint); + Q_DECLARE_FLAGS(DisplayHints, DisplayHint) AbstractNavigationWidget(); ~AbstractNavigationWidget() override; void setContext(NavigationContextPointer context, int initBrowser = 400); void setDisplayHints(DisplayHints hints); QSize sizeHint() const override; public slots: ///Keyboard-action "next" void next() override; ///Keyboard-action "previous" void previous() override; ///Keyboard-action "accept" void accept() override; void up() override; void down() override; virtual void back(); ///These are temporarily for gettings these events directly from kate ///@todo Do this through a public interface post 4.2 void embeddedWidgetRight(); ///Keyboard-action "previous" void embeddedWidgetLeft(); ///Keyboard-action "accept" void embeddedWidgetAccept(); void embeddedWidgetUp(); void embeddedWidgetDown(); NavigationContextPointer context(); Q_SIGNALS: void sizeHintChanged(); /// Emitted whenever the current navigation-context has changed /// @param wasInitial whether the previous context was the initial context /// @param isInitial whether the current context is the initial context void contextChanged(bool wasInitial, bool isInitial); public slots: void navigateDeclaration(KDevelop::IndexedDeclaration decl); private slots: void anchorClicked(const QUrl&); protected: void wheelEvent(QWheelEvent* ) override; void updateIdealSize() const; void initBrowser(int height); void update(); NavigationContextPointer m_startContext; TopDUContextPointer m_topContext; QPointer m_browser; QWidget* m_currentWidget; QString m_currentText; mutable QSize m_idealTextSize; DisplayHints m_hints = NoHints; private: NavigationContextPointer m_context; }; } #endif diff --git a/plugins/problemreporter/problemreporterplugin.cpp b/plugins/problemreporter/problemreporterplugin.cpp index c6bf374a9f..c229a605fe 100644 --- a/plugins/problemreporter/problemreporterplugin.cpp +++ b/plugins/problemreporter/problemreporterplugin.cpp @@ -1,205 +1,205 @@ /* * KDevelop Problem Reporter * * Copyright 2006 Adam Treat * Copyright 2006-2007 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemreporterplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "problemhighlighter.h" #include "problemtreeview.h" #include "problemreportermodel.h" #include "language/assistant/staticassistantsmanager.h" #include #include #include #include #include #include #include "shell/problemmodelset.h" #include "problemsview.h" #include Q_LOGGING_CATEGORY(PLUGIN_PROBLEMREPORTER, "kdevplatform.plugins.problemreporter") K_PLUGIN_FACTORY_WITH_JSON(KDevProblemReporterFactory, "kdevproblemreporter.json", registerPlugin();) using namespace KDevelop; class ProblemReporterFactory : public KDevelop::IToolViewFactory { public: QWidget* create(QWidget* parent = 0) override { Q_UNUSED(parent); ProblemsView* v = new ProblemsView(); v->load(); return v; } Qt::DockWidgetArea defaultPosition() override { return Qt::BottomDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.ProblemReporterView"); } }; ProblemReporterPlugin::ProblemReporterPlugin(QObject* parent, const QVariantList&) : KDevelop::IPlugin(QStringLiteral("kdevproblemreporter"), parent) , m_factory(new ProblemReporterFactory) , m_model(new ProblemReporterModel(this)) { KDevelop::ProblemModelSet* pms = core()->languageController()->problemModelSet(); pms->addModel(QStringLiteral("Parser"), m_model); core()->uiController()->addToolView(i18n("Problems"), m_factory); setXMLFile(QStringLiteral("kdevproblemreporter.rc")); connect(ICore::self()->documentController(), &IDocumentController::documentClosed, this, &ProblemReporterPlugin::documentClosed); connect(ICore::self()->documentController(), &IDocumentController::textDocumentCreated, this, &ProblemReporterPlugin::textDocumentCreated); connect(DUChain::self(), &DUChain::updateReady, this, &ProblemReporterPlugin::updateReady); connect(ICore::self()->languageController()->staticAssistantsManager(), &StaticAssistantsManager::problemsChanged, this, &ProblemReporterPlugin::updateHighlight); } ProblemReporterPlugin::~ProblemReporterPlugin() { qDeleteAll(m_highlighters); } ProblemReporterModel* ProblemReporterPlugin::model() const { return m_model; } void ProblemReporterPlugin::unload() { KDevelop::ProblemModelSet* pms = KDevelop::ICore::self()->languageController()->problemModelSet(); pms->removeModel(QStringLiteral("Parser")); core()->uiController()->removeToolView(m_factory); } void ProblemReporterPlugin::documentClosed(IDocument* doc) { if (!doc->textDocument()) return; IndexedString url(doc->url()); delete m_highlighters.take(url); } void ProblemReporterPlugin::textDocumentCreated(KDevelop::IDocument* document) { Q_ASSERT(document->textDocument()); m_highlighters.insert(IndexedString(document->url()), new ProblemHighlighter(document->textDocument())); DUChain::self()->updateContextForUrl(IndexedString(document->url()), KDevelop::TopDUContext::AllDeclarationsContextsAndUses, this); } -void ProblemReporterPlugin::updateReady(const IndexedString& url, const KDevelop::ReferencedTopDUContext& top) +void ProblemReporterPlugin::updateReady(const IndexedString& url, const KDevelop::ReferencedTopDUContext&) { m_model->problemsUpdated(url); updateHighlight(url); } void ProblemReporterPlugin::updateHighlight(const KDevelop::IndexedString& url) { ProblemHighlighter* ph = m_highlighters.value(url); if (ph) { auto allProblems = m_model->problems(url, false); ph->setProblems(allProblems); } } KDevelop::ContextMenuExtension ProblemReporterPlugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::ContextMenuExtension extension; KDevelop::EditorContext* editorContext = dynamic_cast(context); if (editorContext) { DUChainReadLocker lock(DUChain::lock(), 1000); if (!lock.locked()) { qCDebug(PLUGIN_PROBLEMREPORTER) << "failed to lock duchain in time"; return extension; } QString title; QList actions; TopDUContext* top = DUChainUtils::standardContextForUrl(editorContext->url()); if (top) { foreach (KDevelop::ProblemPointer problem, top->problems()) { if (problem->range().contains( top->transformToLocalRevision(KTextEditor::Cursor(editorContext->position())))) { KDevelop::IAssistant::Ptr solution = problem->solutionAssistant(); if (solution) { title = solution->title(); foreach (KDevelop::IAssistantAction::Ptr action, solution->actions()) actions << action->toKAction(); } } } } if (!actions.isEmpty()) { QString text; if (title.isEmpty()) text = i18n("Solve Problem"); else { text = i18n("Solve: %1", KDevelop::htmlToPlainText(title)); } QAction* menuAction = new QAction(text, 0); QMenu* menu(new QMenu(text, 0)); menuAction->setMenu(menu); foreach (QAction* action, actions) menu->addAction(action); extension.addAction(ContextMenuExtension::ExtensionGroup, menuAction); } } return extension; } #include "problemreporterplugin.moc" // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/util/activetooltip.cpp b/util/activetooltip.cpp index 29c4776239..439d24fda2 100644 --- a/util/activetooltip.cpp +++ b/util/activetooltip.cpp @@ -1,335 +1,339 @@ /* This file is part of the KDE project Copyright 2007 Vladimir Prus Copyright 2009-2010 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "activetooltip.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { class ActiveToolTipManager : public QObject { Q_OBJECT public Q_SLOTS: void doVisibility(); public: typedef QMultiMap, QString> > ToolTipPriorityMap; ToolTipPriorityMap registeredToolTips; }; ActiveToolTipManager* manager() { static ActiveToolTipManager m; return &m; } QWidget* masterWidget(QWidget* w) { while (w && w->parent() && qobject_cast(w->parent())) { w = qobject_cast(w->parent()); } return w; } void ActiveToolTipManager::doVisibility() { bool exclusive = false; int lastBottomPosition = -1; int lastLeftPosition = -1; QRect fullGeometry; //Geometry of all visible tooltips together for (auto it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) { QPointer< ActiveToolTip > w = (*it).first; if (w) { if (exclusive) { (w.data())->hide(); } else { QRect geom = (w.data())->geometry(); if ((w.data())->geometry().top() < lastBottomPosition) { geom.moveTop(lastBottomPosition); } if (lastLeftPosition != -1) { geom.moveLeft(lastLeftPosition); } (w.data())->setGeometry(geom); lastBottomPosition = (w.data())->geometry().bottom(); lastLeftPosition = (w.data())->geometry().left(); if (it == registeredToolTips.constBegin()) { fullGeometry = (w.data())->geometry(); } else { fullGeometry = fullGeometry.united((w.data())->geometry()); } } if (it.key() == 0) { exclusive = true; } } } if (!fullGeometry.isEmpty()) { QRect oldFullGeometry = fullGeometry; QRect screenGeometry = QApplication::desktop()->screenGeometry(fullGeometry.topLeft()); if (fullGeometry.bottom() > screenGeometry.bottom()) { //Move up, avoiding the mouse-cursor fullGeometry.moveBottom(fullGeometry.top()-10); if (fullGeometry.adjusted(-20, -20, 20, 20).contains(QCursor::pos())) { fullGeometry.moveBottom(QCursor::pos().y() - 20); } } if (fullGeometry.right() > screenGeometry.right()) { //Move to left, avoiding the mouse-cursor fullGeometry.moveRight(fullGeometry.left()-10); if (fullGeometry.adjusted(-20, -20, 20, 20).contains(QCursor::pos())) { fullGeometry.moveRight(QCursor::pos().x() - 20); } } // Now fit this to screen if (fullGeometry.left() < 0) { fullGeometry.setLeft(0); } if (fullGeometry.top() < 0) { fullGeometry.setTop(0); } QPoint offset = fullGeometry.topLeft() - oldFullGeometry.topLeft(); if (!offset.isNull()) { for (auto it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) if (it->first) { it->first.data()->move(it->first.data()->pos() + offset); } } } + //Always include the mouse cursor in the full geometry, to avoid + //closing the tooltip inexpectedly + fullGeometry = fullGeometry.united(QRect(QCursor::pos(), QCursor::pos())); + //Set bounding geometry, and remove old tooltips for (auto it = registeredToolTips.begin(); it != registeredToolTips.end(); ) { if (!it->first) { it = registeredToolTips.erase(it); } else { it->first.data()->setBoundingGeometry(fullGeometry); ++it; } } //Final step: Show tooltips foreach (const auto& tooltip, registeredToolTips) { if (tooltip.first.data() && masterWidget(tooltip.first.data())->isActiveWindow()) { tooltip.first.data()->show(); } if (exclusive) { break; } } } } namespace KDevelop { class ActiveToolTipPrivate { public: QRect rect_; QRect handleRect_; QList > friendWidgets_; }; } ActiveToolTip::ActiveToolTip(QWidget *parent, const QPoint& position) : QWidget(parent, Qt::ToolTip) , d(new ActiveToolTipPrivate) { Q_ASSERT(parent); setMouseTracking(true); d->rect_ = QRect(position, position); d->rect_.adjust(-10, -10, 10, 10); move(position); QPalette p; // adjust background color to use tooltip colors p.setColor(backgroundRole(), p.color(QPalette::ToolTipBase)); p.setColor(QPalette::Base, p.color(QPalette::ToolTipBase)); // adjust foreground color to use tooltip colors p.setColor(foregroundRole(), p.color(QPalette::ToolTipText)); p.setColor(QPalette::Text, p.color(QPalette::ToolTipText)); setPalette(p); setWindowFlags(Qt::WindowDoesNotAcceptFocus | windowFlags()); qApp->installEventFilter(this); } ActiveToolTip::~ActiveToolTip() { delete d; } bool ActiveToolTip::eventFilter(QObject *object, QEvent *e) { switch (e->type()) { case QEvent::MouseMove: if (underMouse() || insideThis(object)) { return false; } else { QPoint globalPos = static_cast(e)->globalPos(); QRect mergedRegion = d->rect_.united(d->handleRect_); if (mergedRegion.contains(globalPos)) { return false; } close(); } break; case QEvent::WindowActivate: if (insideThis(object)) { return false; } close(); break; case QEvent::WindowBlocked: // Modal dialog activated somewhere, it is the only case where a cursor // move may be missed and the popup has to be force-closed close(); break; default: break; } return false; } void ActiveToolTip::addFriendWidget(QWidget* widget) { d->friendWidgets_.append((QObject*)widget); } bool ActiveToolTip::insideThis(QObject* object) { while (object) { if (dynamic_cast(object)) { return true; } if (object == this || object == (QObject*)this->windowHandle() || d->friendWidgets_.contains(object)) { return true; } object = object->parent(); } // If the object clicked is inside a QQuickWidget, its parent is null even // if it is part of a tool-tip. This check ensures that a tool-tip is never // closed while the mouse is in it return underMouse(); } void ActiveToolTip::showEvent(QShowEvent*) { adjustRect(); } void ActiveToolTip::resizeEvent(QResizeEvent*) { adjustRect(); // set mask from style QStyleOptionFrame opt; opt.init(this); QStyleHintReturnMask mask; if (style()->styleHint( QStyle::SH_ToolTip_Mask, &opt, this, &mask ) && !mask.region.isEmpty()) { setMask( mask.region ); } emit resized(); } void ActiveToolTip::paintEvent(QPaintEvent* event) { QStylePainter painter( this ); painter.setClipRegion( event->region() ); QStyleOptionFrame opt; opt.init(this); painter.drawPrimitive(QStyle::PE_PanelTipLabel, opt); } void ActiveToolTip::setHandleRect(const QRect& rect) { d->handleRect_= rect; } void ActiveToolTip::adjustRect() { // For tooltip widget, geometry() returns global coordinates. QRect r = geometry(); r.adjust(-10, -10, 10, 10); d->rect_ = r; } void ActiveToolTip::setBoundingGeometry(const QRect& geometry) { d->rect_ = geometry; d->rect_.adjust(-10, -10, 10, 10); } void ActiveToolTip::showToolTip(ActiveToolTip* tooltip, float priority, const QString& uniqueId) { auto& registeredToolTips = manager()->registeredToolTips; if (!uniqueId.isEmpty()) { foreach (const auto& tooltip, registeredToolTips) { if (tooltip.second == uniqueId) { delete tooltip.first.data(); } } } registeredToolTips.insert(priority, qMakePair(QPointer(tooltip), uniqueId)); connect(tooltip, &ActiveToolTip::resized, manager(), &ActiveToolTipManager::doVisibility); QMetaObject::invokeMethod(manager(), "doVisibility", Qt::QueuedConnection); } void ActiveToolTip::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); deleteLater(); } #include "activetooltip.moc"