2 * This file is part of KDevelop
4 * Copyright 2008 David Nolden <david.nolden.kdevelop@art-master.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "browsemanager.h"
23 #include <QMouseEvent>
24 #include <QToolButton>
26 #include <ktexteditor/view.h>
27 #include <ktexteditor/document.h>
28 #include <interfaces/icore.h>
29 #include <interfaces/idocumentcontroller.h>
30 #include "contextbrowserview.h"
31 #include <ktexteditor/smartinterface.h>
32 #include <interfaces/ilanguage.h>
33 #include <interfaces/ilanguagecontroller.h>
34 #include <language/interfaces/ilanguagesupport.h>
35 #include <language/editor/simplecursor.h>
36 #include <language/duchain/duchainutils.h>
37 #include <language/duchain/duchainlock.h>
38 #include <language/duchain/duchain.h>
39 #include <language/duchain/declaration.h>
41 using namespace KDevelop
;
42 using namespace KTextEditor
;
44 static QWidget
* masterWidget(QWidget
* w
) {
45 while(w
&& w
->parentWidget())
46 w
= w
->parentWidget();
50 EditorViewWatcher::EditorViewWatcher(QWidget
* sameWindow
) : m_childrenOf(masterWidget(sameWindow
)) {
52 connect(ICore::self()->documentController(), SIGNAL(textDocumentCreated(KDevelop::IDocument
*)), this, SLOT(documentCreated(KDevelop::IDocument
*)));
53 foreach(KDevelop::IDocument
* document
, ICore::self()->documentController()->openDocuments())
54 documentCreated(document
);
57 void EditorViewWatcher::documentCreated( KDevelop::IDocument
* document
) {
58 KTextEditor::Document
* textDocument
= document
->textDocument();
60 connect(textDocument
, SIGNAL(viewCreated(KTextEditor::Document
*, KTextEditor::View
*)), this, SLOT(viewCreated(KTextEditor::Document
*, KTextEditor::View
*)));
61 foreach(KTextEditor::View
* view
, textDocument
->views()) {
62 Q_ASSERT(view
->parentWidget());
63 // if(!m_childrenOf || masterWidget(view) == m_childrenOf)
64 addViewInternal(view
);
69 void EditorViewWatcher::addViewInternal(KTextEditor::View
* view
) {
72 connect(view
, SIGNAL(destroyed(QObject
*)), this, SLOT(viewDestroyed(QObject
*)));
75 void EditorViewWatcher::viewAdded(KTextEditor::View
*) {
78 void EditorViewWatcher::viewDestroyed(QObject
* view
) {
79 m_views
.removeAll(static_cast<KTextEditor::View
*>(view
));
82 void EditorViewWatcher::viewCreated(KTextEditor::Document
* /*doc*/, KTextEditor::View
* view
) {
83 Q_ASSERT(view
->parentWidget());
84 //The test doesn't work porperly at this point
85 // if(!m_childrenOf || masterWidget(view) == m_childrenOf)
86 addViewInternal(view
);
89 QList
<KTextEditor::View
*> EditorViewWatcher::allViews() {
93 BrowseManager::BrowseManager(ContextController
* controller
) : QObject(controller
), m_controller(controller
), m_watcher(this), m_browsing(false), m_browsingByKey(false) {
96 KTextEditor::View
* viewFromWidget(QWidget
* widget
) {
99 KTextEditor::View
* view
= qobject_cast
<KTextEditor::View
*>(widget
);
103 return viewFromWidget(widget
->parentWidget());
106 bool BrowseManager::eventFilter(QObject
* watched
, QEvent
* event
) {
107 QWidget
* widget
= qobject_cast
<QWidget
*>(watched
);
109 KTextEditor::View
* view
= viewFromWidget(widget
);
113 const int browseKey
= Qt::Key_Control
;
115 //Eventually start key-browsing
116 QKeyEvent
* keyEvent
= dynamic_cast<QKeyEvent
*>(event
);
117 if(keyEvent
&& keyEvent
->key() == browseKey
&& !m_browsingByKey
&& keyEvent
->type() == QEvent::KeyPress
) {
118 m_browsingByKey
= true;
120 m_controller
->browseButton()->setChecked(true);
124 QFocusEvent
* focusEvent
= dynamic_cast<QFocusEvent
*>(event
);
126 //Eventually stop key-browsing
127 if((keyEvent
&& keyEvent
->key() == browseKey
&& m_browsingByKey
&& keyEvent
->type() == QEvent::KeyRelease
) ||
128 (focusEvent
&& focusEvent
->lostFocus())) {
130 m_controller
->browseButton()->setChecked(false);
131 m_browsingByKey
= false;
134 if(!m_browsing
&& !m_browsingByKey
) {
135 resetChangedCursor();
139 QMouseEvent
* mouseEvent
= dynamic_cast<QMouseEvent
*>(event
);
141 KTextEditor::CoordinatesToCursorInterface
* iface
= dynamic_cast<KTextEditor::CoordinatesToCursorInterface
*>(view
);
143 kDebug() << "Update kdelibs for the browsing-mode to work";
147 QPoint coordinatesInView
= widget
->mapTo(view
, mouseEvent
->pos());
149 KTextEditor::Cursor textCursor
= iface
->coordinatesToCursor(coordinatesInView
);
150 if(textCursor
.isValid()) {
151 SmartInterface
* iface
= dynamic_cast<SmartInterface
*>(view
->document());
152 if (!iface
) return false;
154 ///@todo find out why this is needed, fix the code in kate
155 if(textCursor
.column() > 0)
156 textCursor
.setColumn(textCursor
.column()-1);
158 KUrl viewUrl
= view
->document()->url();
159 QList
<ILanguage
*> languages
= ICore::self()->languageController()->languagesForUrl(viewUrl
);
161 QPair
<KUrl
, KDevelop::SimpleCursor
> jumpTo
;
163 //Step 1: Look for a special language object(Macro, included header, etc.)
164 foreach( ILanguage
* language
, languages
) {
165 jumpTo
= language
->languageSupport()->specialLanguageObjectJumpCursor(viewUrl
, SimpleCursor(textCursor
));
166 if(jumpTo
.first
.isValid() && jumpTo
.second
.isValid())
167 break; //Found a special object to jump to
170 //Step 2: Look for a declaration/use
171 if(!jumpTo
.first
.isValid() || !jumpTo
.second
.isValid()) {
172 Declaration
* foundDeclaration
= 0;
173 KDevelop::DUChainReadLocker
lock( DUChain::lock() );
174 foundDeclaration
= DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(view
->document()->url(), SimpleCursor(textCursor
)) );
176 if( foundDeclaration
) {
177 jumpTo
.first
= foundDeclaration
->url().toUrl();
178 jumpTo
.second
= foundDeclaration
->range().start
;
181 if(jumpTo
.first
.isValid() && jumpTo
.second
.isValid()) {
182 if(mouseEvent
->button() == Qt::LeftButton
&& mouseEvent
->type() == QEvent::MouseButtonPress
) {
183 ICore::self()->documentController()->openDocument(jumpTo
.first
, jumpTo
.second
.textCursor());
186 }else if(mouseEvent
->type() == QEvent::MouseMove
) {
187 //Make the cursor a "hand"
188 setHandCursor(widget
);
193 resetChangedCursor();
198 void BrowseManager::resetChangedCursor() {
199 QMap
<QPointer
<QWidget
>, QCursor
> cursors
= m_oldCursors
;
200 m_oldCursors
.clear();
202 for(QMap
<QPointer
<QWidget
>, QCursor
>::iterator it
= cursors
.begin(); it
!= cursors
.end(); ++it
)
204 it
.key()->setCursor(QCursor(Qt::IBeamCursor
));
207 void BrowseManager::setHandCursor(QWidget
* widget
) {
208 if(m_oldCursors
.contains(widget
))
209 return; //Nothing to do
210 m_oldCursors
[widget
] = widget
->cursor();
211 widget
->setCursor(QCursor(Qt::PointingHandCursor
));
214 void BrowseManager::applyEventFilter(QWidget
* object
, bool install
) {
216 object
->installEventFilter(this);
218 object
->removeEventFilter(this);
220 foreach(QObject
* child
, object
->children())
221 if(qobject_cast
<QWidget
*>(child
))
222 applyEventFilter(qobject_cast
<QWidget
*>(child
), install
);
225 void BrowseManager::Watcher::viewAdded(KTextEditor::View
* view
) {
226 m_manager
->applyEventFilter(view
, true);
229 void BrowseManager::setBrowsing(bool enabled
) {
232 if(enabled
== m_browsing
)
234 m_browsing
= enabled
;
236 //This collects all the views
238 kDebug() << "Enabled browsing-mode";
239 ///Enable browsing, install an event-filter on all events
240 foreach(KTextEditor::View
* view
, m_watcher
.allViews())
241 applyEventFilter(view
, true);
243 kDebug() << "Disabled browsing-mode";
244 resetChangedCursor();
248 BrowseManager::Watcher::Watcher(BrowseManager
* manager
) : EditorViewWatcher(masterWidget(manager
->m_controller
->view())), m_manager(manager
) {
249 foreach(KTextEditor::View
* view
, allViews())
250 m_manager
->applyEventFilter(view
, true);