1 /*
2     SPDX-FileCopyrightText: 2006 Adam Treat <treat@kde.org>
3     SPDX-FileCopyrightText: 2006-2007 Hamish Rodda <rodda@kde.org>
4     SPDX-FileCopyrightText: 2009 Lior Mualem <lior.m.kde@gmail.com>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "classtree.h"
10 
11 #include <QMenu>
12 #include <QHeaderView>
13 #include <QContextMenuEvent>
14 #include <QScrollBar>
15 
16 #include "interfaces/contextmenuextension.h"
17 #include "interfaces/icore.h"
18 #include "interfaces/idocument.h"
19 #include "interfaces/iplugincontroller.h"
20 
21 #include "language/interfaces/codecontext.h"
22 
23 #include "language/duchain/duchainbase.h"
24 #include "language/duchain/duchain.h"
25 #include "language/duchain/duchainlock.h"
26 #include "language/duchain/declaration.h"
27 
28 #include "language/classmodel/classmodel.h"
29 #include "classbrowserplugin.h"
30 
31 using namespace KDevelop;
32 
ClassTree(QWidget * parent,ClassBrowserPlugin * plugin)33 ClassTree::ClassTree(QWidget* parent, ClassBrowserPlugin* plugin)
34     : QTreeView(parent)
35     , m_plugin(plugin)
36     , m_tooltip(nullptr)
37 {
38     header()->hide();
39 
40     setIndentation(10);
41     setUniformRowHeights(true);
42 
43     connect(this, &ClassTree::activated, this, &ClassTree::itemActivated);
44 }
45 
~ClassTree()46 ClassTree::~ClassTree()
47 {
48 }
49 
50 static bool _populatingClassBrowserContextMenu = false;
51 
populatingClassBrowserContextMenu()52 bool ClassTree::populatingClassBrowserContextMenu()
53 {
54     return _populatingClassBrowserContextMenu;
55 }
56 
contextMenuEvent(QContextMenuEvent * e)57 void ClassTree::contextMenuEvent(QContextMenuEvent* e)
58 {
59     auto* menu = new QMenu(this);
60     QModelIndex index = indexAt(e->pos());
61     if (index.isValid()) {
62         Context* c;
63         {
64             DUChainReadLocker readLock(DUChain::lock());
65             if (auto* decl = dynamic_cast<Declaration*>(model()->duObjectForIndex(index)))
66                 c = new DeclarationContext(decl);
67             else
68             {
69                 delete menu;
70                 return;
71             }
72         }
73         _populatingClassBrowserContextMenu = true;
74 
75         QList<ContextMenuExtension> extensions =
76             ICore::self()->pluginController()->queryPluginsForContextMenuExtensions(c, menu);
77         ContextMenuExtension::populateMenu(menu, extensions);
78 
79         _populatingClassBrowserContextMenu = false;
80     }
81 
82     if (!menu->actions().isEmpty())
83         menu->exec(e->globalPos());
84     delete menu;
85 }
86 
event(QEvent * event)87 bool ClassTree::event(QEvent* event)
88 {
89     if (event->type() == QEvent::ToolTip) {
90         // if we request a tooltip over a duobject item, show a tooltip for it
91         auto* helpEvent = static_cast<QHelpEvent*>(event);
92         const QModelIndex idxView = indexAt(helpEvent->pos());
93 
94         DUChainReadLocker readLock(DUChain::lock());
95         if (auto* decl = dynamic_cast<Declaration*>(model()->duObjectForIndex(idxView))) {
96             if (m_tooltip) {
97                 m_tooltip->close();
98             }
99             if (auto* navigationWidget = decl->topContext()->createNavigationWidget(decl)) {
100                 m_tooltip = new KDevelop::NavigationToolTip(this, helpEvent->globalPos() + QPoint(40,
101                                                                                                   0), navigationWidget);
102                 m_tooltip->resize(navigationWidget->sizeHint() + QSize(10, 10));
103                 ActiveToolTip::showToolTip(m_tooltip);
104                 return true;
105             }
106         }
107     }
108 
109     return QAbstractItemView::event(event);
110 }
111 
model()112 ClassModel* ClassTree::model()
113 {
114     return static_cast<ClassModel*>(QTreeView::model());
115 }
116 
itemActivated(const QModelIndex & index)117 void ClassTree::itemActivated(const QModelIndex& index)
118 {
119     DUChainReadLocker readLock(DUChain::lock());
120 
121     DeclarationPointer decl = DeclarationPointer(dynamic_cast<Declaration*>(model()->duObjectForIndex(index)));
122     readLock.unlock();
123 
124 // Delegate to plugin function
125     m_plugin->showDefinition(decl);
126 
127     if (isExpanded(index))
128         collapse(index);
129     else
130         expand(index);
131 }
132 
highlightIdentifier(const KDevelop::IndexedQualifiedIdentifier & a_id)133 void ClassTree::highlightIdentifier(const KDevelop::IndexedQualifiedIdentifier& a_id)
134 {
135     QModelIndex index = model()->indexForIdentifier(a_id);
136     if (!index.isValid())
137         return;
138 
139     // expand and select the item.
140     selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
141     scrollTo(index, PositionAtCenter);
142     horizontalScrollBar()->setValue(horizontalScrollBar()->minimum());
143     expand(index);
144 }
145