File indexing completed on 2024-05-19 12:23:34
0001 /* 0002 SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "contextbrowserview.h" 0008 0009 #include <QVBoxLayout> 0010 #include <QIcon> 0011 #include <QMenu> 0012 #include <QShowEvent> 0013 #include <QTextBrowser> 0014 0015 #include <KToggleAction> 0016 #include <KLocalizedString> 0017 #include <KTextEditor/Document> 0018 0019 #include <language/duchain/declaration.h> 0020 #include <language/duchain/topducontext.h> 0021 #include <language/duchain/ducontext.h> 0022 #include <language/duchain/duchain.h> 0023 #include <language/duchain/duchainlock.h> 0024 0025 #include <interfaces/icore.h> 0026 0027 #include "contextbrowser.h" 0028 #include "debug.h" 0029 #include <language/duchain/duchainutils.h> 0030 #include <language/duchain/types/functiontype.h> 0031 #include <language/duchain/specializationstore.h> 0032 #include "browsemanager.h" 0033 #include <language/duchain/navigation/abstractnavigationwidget.h> 0034 #include <language/interfaces/codecontext.h> 0035 #include <language/duchain/navigation/abstractdeclarationnavigationcontext.h> 0036 #include <language/duchain/navigation/useswidget.h> 0037 #include <interfaces/contextmenuextension.h> 0038 #include <interfaces/iplugincontroller.h> 0039 0040 using namespace KDevelop; 0041 0042 namespace { 0043 enum Direction 0044 { 0045 NextUse, 0046 PreviousUse 0047 }; 0048 0049 void selectUse(ContextBrowserView* view, Direction direction) 0050 { 0051 auto abstractNaviWidget = qobject_cast<AbstractNavigationWidget*>(view->navigationWidget()); 0052 0053 if (!abstractNaviWidget) { 0054 return; 0055 } 0056 0057 auto usesWidget = qobject_cast<UsesWidget*>(abstractNaviWidget->context()->widget()); 0058 if (!usesWidget) { 0059 return; 0060 } 0061 0062 OneUseWidget* first = nullptr, * previous = nullptr, * current = nullptr; 0063 const auto& usesWidgetItems = usesWidget->items(); 0064 for (auto item : usesWidgetItems) { 0065 auto topContext = qobject_cast<TopContextUsesWidget*>(item); 0066 if (!topContext) { 0067 continue; 0068 } 0069 const auto& topContextItems = topContext->items(); 0070 for (auto item : topContextItems) { 0071 auto navigationList = qobject_cast<NavigatableWidgetList*>(item); 0072 if (!navigationList) { 0073 continue; 0074 } 0075 const auto& navigationListItems = navigationList->items(); 0076 for (auto item : navigationListItems) { 0077 auto use = qobject_cast<OneUseWidget*>(item); 0078 if (!use) { 0079 continue; 0080 } 0081 if (!first) { 0082 first = use; 0083 } 0084 current = use; 0085 if (direction == PreviousUse && current->isHighlighted() && previous) { 0086 previous->setHighlighted(true); 0087 previous->activateLink(); 0088 current->setHighlighted(false); 0089 return; 0090 } 0091 if (direction == NextUse && previous && previous->isHighlighted()) { 0092 current->setHighlighted(true); 0093 current->activateLink(); 0094 previous->setHighlighted(false); 0095 return; 0096 } 0097 previous = current; 0098 } 0099 } 0100 } 0101 0102 if (direction == NextUse && first) { 0103 first->setHighlighted(true); 0104 first->activateLink(); 0105 if (current && current->isHighlighted()) 0106 current->setHighlighted(false); 0107 return; 0108 } 0109 if (direction == PreviousUse && current) { 0110 current->setHighlighted(true); 0111 current->activateLink(); 0112 if (first && first->isHighlighted()) { 0113 first->setHighlighted(false); 0114 } 0115 } 0116 } 0117 } 0118 0119 AbstractNavigationWidget* ContextBrowserView::createWidget(KDevelop::DUContext* context) 0120 { 0121 m_context = IndexedDUContext(context); 0122 if (m_context.data()) { 0123 return m_context.data()->createNavigationWidget(nullptr, nullptr, AbstractNavigationWidget::EmbeddableWidget); 0124 } 0125 return nullptr; 0126 } 0127 0128 KDevelop::IndexedDeclaration ContextBrowserView::declaration() const 0129 { 0130 return m_declaration; 0131 } 0132 0133 AbstractNavigationWidget* ContextBrowserView::createWidget(Declaration* decl, TopDUContext* topContext) 0134 { 0135 m_declaration = IndexedDeclaration(decl); 0136 return decl->context()->createNavigationWidget(decl, topContext, AbstractNavigationWidget::EmbeddableWidget); 0137 } 0138 0139 void ContextBrowserView::resetWidget() 0140 { 0141 if (m_navigationWidget) { 0142 delete m_navigationWidget; 0143 m_navigationWidget = nullptr; 0144 } 0145 } 0146 0147 void ContextBrowserView::declarationMenu() 0148 { 0149 DUChainReadLocker lock(DUChain::lock()); 0150 0151 auto* navigationWidget = qobject_cast<AbstractNavigationWidget*>(m_navigationWidget.data()); 0152 if (navigationWidget) { 0153 auto* navigationContext = qobject_cast<AbstractDeclarationNavigationContext*>(navigationWidget->context().data()); 0154 if (navigationContext && navigationContext->declaration().data()) { 0155 auto* c = new KDevelop::DeclarationContext(navigationContext->declaration().data()); 0156 lock.unlock(); 0157 QMenu menu(this); 0158 QList<ContextMenuExtension> extensions = 0159 ICore::self()->pluginController()->queryPluginsForContextMenuExtensions(c, &menu); 0160 0161 ContextMenuExtension::populateMenu(&menu, extensions); 0162 menu.exec(QCursor::pos()); 0163 } 0164 } 0165 } 0166 0167 ContextBrowserView::ContextBrowserView(ContextBrowserPlugin* plugin, QWidget* parent) : QWidget(parent) 0168 , m_plugin(plugin) 0169 , m_navigationWidget(new QTextBrowser()) 0170 , m_autoLocked(false) 0171 { 0172 setWindowTitle(i18nc("@title:window", "Code Browser")); 0173 setWindowIcon(QIcon::fromTheme(QStringLiteral("code-context"), windowIcon())); 0174 0175 m_allowLockedUpdate = false; 0176 0177 m_declarationMenuAction = new QAction(QIcon::fromTheme(QStringLiteral("code-class")), QString(), this); 0178 m_declarationMenuAction->setToolTip(i18nc("@info:tooltip", "Show declaration menu")); 0179 // expose the declaration menu via the context menu; allows hiding the toolbar to save some space 0180 // (this will not make it behave like a submenu though) 0181 m_declarationMenuAction->setText(i18nc("@action", "Declaration Menu")); 0182 connect(m_declarationMenuAction, &QAction::triggered, this, &ContextBrowserView::declarationMenu); 0183 addAction(m_declarationMenuAction); 0184 m_lockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-unlocked")), 0185 i18nc("@action", "Lock Current View"), this); 0186 m_lockAction->setToolTip(i18nc("@info:tooltip", "Lock current view")); 0187 m_lockAction->setCheckedState(KGuiItem(i18nc("@action", "Unlock Current View"), 0188 QIcon::fromTheme(QStringLiteral("object-locked")), 0189 i18nc("@info:tooltip", "Unlock current view"))); 0190 m_lockAction->setChecked(false); 0191 addAction(m_lockAction); 0192 0193 m_layout = new QVBoxLayout; 0194 m_layout->setSpacing(0); 0195 m_layout->setContentsMargins(0, 0, 0, 0); 0196 m_layout->addWidget(m_navigationWidget); 0197 //m_layout->addStretch(); 0198 setLayout(m_layout); 0199 0200 m_plugin->registerToolView(this); 0201 } 0202 0203 ContextBrowserView::~ContextBrowserView() 0204 { 0205 m_plugin->unRegisterToolView(this); 0206 } 0207 0208 void ContextBrowserView::focusInEvent(QFocusEvent* event) 0209 { 0210 //Indicate that we have focus 0211 qCDebug(PLUGIN_CONTEXTBROWSER) << "got focus"; 0212 // parentWidget()->setBackgroundRole(QPalette::ToolTipBase); 0213 /* m_layout->removeItem(m_buttons);*/ 0214 0215 QWidget::focusInEvent(event); 0216 } 0217 0218 void ContextBrowserView::focusOutEvent(QFocusEvent* event) 0219 { 0220 qCDebug(PLUGIN_CONTEXTBROWSER) << "lost focus"; 0221 // parentWidget()->setBackgroundRole(QPalette::Background); 0222 /* m_layout->insertLayout(0, m_buttons); 0223 for(int a = 0; a < m_buttons->count(); ++a) { 0224 QWidgetItem* item = dynamic_cast<QWidgetItem*>(m_buttons->itemAt(a)); 0225 }*/ 0226 QWidget::focusOutEvent(event); 0227 } 0228 0229 bool ContextBrowserView::event(QEvent* event) 0230 { 0231 auto* keyEvent = dynamic_cast<QKeyEvent*>(event); 0232 0233 if (hasFocus() && keyEvent) { 0234 auto* navigationWidget = qobject_cast<AbstractNavigationWidget*>(m_navigationWidget.data()); 0235 if (navigationWidget && event->type() == QEvent::KeyPress) { 0236 int key = keyEvent->key(); 0237 if (key == Qt::Key_Left) 0238 navigationWidget->previous(); 0239 if (key == Qt::Key_Right) 0240 navigationWidget->next(); 0241 if (key == Qt::Key_Up) 0242 navigationWidget->up(); 0243 if (key == Qt::Key_Down) 0244 navigationWidget->down(); 0245 if (key == Qt::Key_Return || key == Qt::Key_Enter) 0246 navigationWidget->accept(); 0247 0248 if (key == Qt::Key_L) 0249 m_lockAction->toggle(); 0250 } 0251 } 0252 return QWidget::event(event); 0253 } 0254 0255 void ContextBrowserView::showEvent(QShowEvent* event) 0256 { 0257 DUChainReadLocker lock(DUChain::lock(), 200); 0258 if (!lock.locked()) { 0259 QWidget::showEvent(event); 0260 return; 0261 } 0262 0263 TopDUContext* top = m_lastUsedTopContext.data(); 0264 if (top && m_navigationWidgetDeclaration.isValid()) { 0265 //Update the navigation-widget 0266 Declaration* decl = m_navigationWidgetDeclaration.declaration(top); 0267 if (decl) 0268 setDeclaration(decl, top, true); 0269 } 0270 QWidget::showEvent(event); 0271 } 0272 0273 bool ContextBrowserView::isLocked() const 0274 { 0275 bool isLocked; 0276 if (m_allowLockedUpdate) { 0277 isLocked = false; 0278 } else { 0279 isLocked = m_lockAction->isChecked(); 0280 } 0281 return isLocked; 0282 } 0283 0284 void ContextBrowserView::updateMainWidget(QWidget* widget) 0285 { 0286 if (widget) { 0287 setUpdatesEnabled(false); 0288 qCDebug(PLUGIN_CONTEXTBROWSER) << ""; 0289 resetWidget(); 0290 m_navigationWidget = widget; 0291 m_layout->insertWidget(1, widget, 1); 0292 m_allowLockedUpdate = false; 0293 setUpdatesEnabled(true); 0294 if (widget->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("contextChanged(bool,bool)").constData()) 0295 != -1) { 0296 connect(widget, SIGNAL(contextChanged(bool,bool)), this, SLOT(navigationContextChanged(bool,bool))); 0297 } 0298 } 0299 } 0300 0301 void ContextBrowserView::navigationContextChanged(bool wasInitial, bool isInitial) 0302 { 0303 if (wasInitial && !isInitial && !m_lockAction->isChecked()) { 0304 m_autoLocked = true; 0305 m_lockAction->setChecked(true); 0306 } else if (!wasInitial && isInitial && m_autoLocked) { 0307 m_autoLocked = false; 0308 m_lockAction->setChecked(false); 0309 } else if (isInitial) { 0310 m_autoLocked = false; 0311 } 0312 } 0313 0314 void ContextBrowserView::selectNextItem() 0315 { 0316 selectUse(this, NextUse); 0317 } 0318 0319 void ContextBrowserView::selectPreviousItem() 0320 { 0321 selectUse(this, PreviousUse); 0322 } 0323 0324 void ContextBrowserView::setDeclaration(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext, bool force) 0325 { 0326 m_lastUsedTopContext = IndexedTopDUContext(topContext); 0327 0328 if (isLocked() && (!m_navigationWidget.data() || !isVisible())) { 0329 // Automatically remove the locked state if the view is not visible or the widget was deleted, 0330 // because the locked state has side-effects on other navigation functionality. 0331 m_autoLocked = false; 0332 m_lockAction->setChecked(false); 0333 } 0334 0335 if (m_navigationWidgetDeclaration == decl->id() && !force) 0336 return; 0337 0338 m_navigationWidgetDeclaration = decl->id(); 0339 0340 if (!isLocked() && (isVisible() || force)) { // NO-OP if tool view is hidden, for performance reasons 0341 updateMainWidget(createWidget(decl, topContext)); 0342 } 0343 } 0344 0345 KDevelop::IndexedDeclaration ContextBrowserView::lockedDeclaration() const 0346 { 0347 if (m_lockAction->isChecked()) 0348 return declaration(); 0349 else 0350 return KDevelop::IndexedDeclaration(); 0351 } 0352 0353 void ContextBrowserView::allowLockedUpdate() 0354 { 0355 m_allowLockedUpdate = true; 0356 } 0357 0358 void ContextBrowserView::setNavigationWidget(QWidget* widget) 0359 { 0360 updateMainWidget(widget); 0361 } 0362 0363 void ContextBrowserView::setContext(KDevelop::DUContext* context) 0364 { 0365 if (!context) 0366 return; 0367 0368 m_lastUsedTopContext = IndexedTopDUContext(context->topContext()); 0369 0370 if (context->owner()) { 0371 if (context->owner()->id() == m_navigationWidgetDeclaration) 0372 return; 0373 m_navigationWidgetDeclaration = context->owner()->id(); 0374 } else { 0375 m_navigationWidgetDeclaration = DeclarationId(); 0376 } 0377 0378 if (!isLocked() && isVisible()) { // NO-OP if tool view is hidden, for performance reasons 0379 updateMainWidget(createWidget(context)); 0380 } 0381 } 0382 0383 void ContextBrowserView::setSpecialNavigationWidget(QWidget* widget) 0384 { 0385 if (!isLocked() && isVisible()) { 0386 Q_ASSERT(widget); 0387 updateMainWidget(widget); 0388 } else if (widget) { 0389 widget->deleteLater(); 0390 } 0391 } 0392 0393 #include "moc_contextbrowserview.cpp"