| Summary: | "Outline" toolbar item needs "Code Browser" toolview enabled | ||
|---|---|---|---|
| Product: | [Developer tools] kdevplatform | Reporter: | Nicolás Alvarez <nalvarez> |
| Component: | contextbrowser | Assignee: | kdevelop-bugs-null |
| Status: | RESOLVED FIXED | ||
| Severity: | major | CC: | ashaduri |
| Priority: | VHI | ||
| Version First Reported In: | 1.1.0 | ||
| Target Milestone: | 1.2.0 | ||
| Platform: | Unlisted Binaries | ||
| OS: | Linux | ||
| Latest Commit: | Version Fixed/Implemented In: | ||
| Sentry Crash Report: | |||
|
Description
Nicolás Alvarez
2010-12-07 01:22:39 UTC
probably only a wrong parent - the widget should be created in the KAction::createWidget method and not take the toolview as parent. it also seems that most other useful features of the context browser plugin (tooltip, actual browsing via ctrl + click, ...) all rely on a view being around. I'll try to fix that later. *** Bug 203962 has been marked as a duplicate of this bug. *** commit 3bc2e9b5f2ed606e5d4d85292473f34b9af20f86 branch 1.2 Author: Milian Wolff <mail@milianw.de> Date: Fri Dec 17 17:03:21 2010 +0100 Refactor context browser plugin to make it useable without a toolview. BUG: 259066 Please, everyone test this as extensively as possible. I didn't spot any issues. But I don't want to ship 4.2 with regressions here. diff --git a/language/util/navigationtooltip.h b/language/util/navigationtooltip.h index 861fc29..2969c3c 100644 --- a/language/util/navigationtooltip.h +++ b/language/util/navigationtooltip.h @@ -27,6 +27,9 @@ namespace KDevelop { class AbstractNavigationWidget; ///A tooltip that just emebed the given widget. +/// +///TODO: 4.3 - cleanup indentation +/// introduce getter for widget, use that in context browser class KDEVPLATFORMLANGUAGE_EXPORT NavigationToolTip : public ActiveToolTip { Q_OBJECT diff --git a/plugins/contextbrowser/browsemanager.cpp b/plugins/contextbrowser/browsemanager.cpp index 65a673c..77ab0c4 100644 --- a/plugins/contextbrowser/browsemanager.cpp +++ b/plugins/contextbrowser/browsemanager.cpp @@ -42,17 +42,14 @@ #include <qtimer.h> #include <qapplication.h> +#include "contextbrowser.h" + using namespace KDevelop; using namespace KTextEditor; -static QWidget* masterWidget(QWidget* w) { - while(w && w->parentWidget()) - w = w->parentWidget(); - return w; -} - -EditorViewWatcher::EditorViewWatcher(QWidget* sameWindow) : m_childrenOf(masterWidget(sameWindow)) { - +EditorViewWatcher::EditorViewWatcher(QObject* parent) + : QObject(parent) +{ connect(ICore::self()->documentController(), SIGNAL(textDocumentCreated(KDevelop::IDocument*)), this, SLOT(documentCreated(KDevelop::IDocument*))); foreach(KDevelop::IDocument* document, ICore::self()->documentController()->openDocuments()) documentCreated(document); @@ -64,8 +61,7 @@ void EditorViewWatcher::documentCreated( KDevelop::IDocument* document ) { connect(textDocument, SIGNAL(viewCreated(KTextEditor::Document*, KTextEditor::View*)), this, SLOT(viewCreated(KTextEditor::Document*, KTextEditor::View*))); foreach(KTextEditor::View* view, textDocument->views()) { Q_ASSERT(view->parentWidget()); -// if(!m_childrenOf || masterWidget(view) == m_childrenOf) - addViewInternal(view); + addViewInternal(view); } } } @@ -85,9 +81,7 @@ void EditorViewWatcher::viewDestroyed(QObject* view) { void EditorViewWatcher::viewCreated(KTextEditor::Document* /*doc*/, KTextEditor::View* view) { Q_ASSERT(view->parentWidget()); - //The test doesn't work porperly at this point -// if(!m_childrenOf || masterWidget(view) == m_childrenOf) - addViewInternal(view); + addViewInternal(view); } QList<KTextEditor::View*> EditorViewWatcher::allViews() { @@ -99,7 +93,7 @@ void BrowseManager::eventuallyStartDelayedBrowsing() { emit startDelayedBrowsing(m_browingStartedInView); } -BrowseManager::BrowseManager(ContextBrowserView* controller) : QObject(controller), m_view(controller), m_browsing(false), m_browsingByKey(0), m_watcher(this) { +BrowseManager::BrowseManager(ContextBrowserPlugin* controller) : QObject(controller), m_plugin(controller), m_browsing(false), m_browsingByKey(0), m_watcher(this) { m_delayedBrowsingTimer = new QTimer(this); m_delayedBrowsingTimer->setSingleShot(true); @@ -157,7 +151,7 @@ bool BrowseManager::eventFilter(QObject * watched, QEvent * event) { } if(!m_browsing) - m_view->setAllowBrowsing(true); + m_plugin->setAllowBrowsing(true); } @@ -167,7 +161,7 @@ bool BrowseManager::eventFilter(QObject * watched, QEvent * event) { if((keyEvent && m_browsingByKey && keyEvent->key() == m_browsingByKey && keyEvent->type() == QEvent::KeyRelease) || (focusEvent && focusEvent->lostFocus())) { if(!m_browsing) - m_view->setAllowBrowsing(false); + m_plugin->setAllowBrowsing(false); m_browsingByKey = 0; emit stopDelayedBrowsing(); } @@ -302,12 +296,12 @@ void BrowseManager::viewAdded(KTextEditor::View* view) { //We need to listen for cursorPositionChanged, to clear the shift-detector. The problem: Kate listens for the arrow-keys using shortcuts, //so those keys are not passed to the event-filter - connect(view, SIGNAL(navigateLeft()), m_view, SLOT(navigateLeft())); - connect(view, SIGNAL(navigateRight()), m_view, SLOT(navigateRight())); - connect(view, SIGNAL(navigateUp()), m_view, SLOT(navigateUp())); - connect(view, SIGNAL(navigateDown()), m_view, SLOT(navigateDown())); - connect(view, SIGNAL(navigateAccept()), m_view, SLOT(navigateAccept())); - connect(view, SIGNAL(navigateBack()), m_view, SLOT(navigateBack())); + connect(view, SIGNAL(navigateLeft()), m_plugin, SLOT(navigateLeft())); + connect(view, SIGNAL(navigateRight()), m_plugin, SLOT(navigateRight())); + connect(view, SIGNAL(navigateUp()), m_plugin, SLOT(navigateUp())); + connect(view, SIGNAL(navigateDown()), m_plugin, SLOT(navigateDown())); + connect(view, SIGNAL(navigateAccept()), m_plugin, SLOT(navigateAccept())); + connect(view, SIGNAL(navigateBack()), m_plugin, SLOT(navigateBack())); } void BrowseManager::Watcher::viewAdded(KTextEditor::View* view) { @@ -330,7 +324,8 @@ void BrowseManager::setBrowsing(bool enabled) { } } -BrowseManager::Watcher::Watcher(BrowseManager* manager) : EditorViewWatcher(masterWidget(manager->m_view)), m_manager(manager) { +BrowseManager::Watcher::Watcher(BrowseManager* manager) + : EditorViewWatcher(manager), m_manager(manager) { foreach(KTextEditor::View* view, allViews()) m_manager->applyEventFilter(view, true); } diff --git a/plugins/contextbrowser/browsemanager.h b/plugins/contextbrowser/browsemanager.h index 566a9e3..acb896d 100644 --- a/plugins/contextbrowser/browsemanager.h +++ b/plugins/contextbrowser/browsemanager.h @@ -43,25 +43,24 @@ namespace KDevelop { class EditorViewWatcher : QObject { Q_OBJECT - public: +public: ///@param sameWindow If this is true, only views that are child of the same window as the given widget are registered - EditorViewWatcher(QWidget* sameWindow = 0); + EditorViewWatcher(QObject* parent = 0); QList<KTextEditor::View*> allViews(); - private: +private: ///Called for every added view. Reimplement this to catch them. virtual void viewAdded(KTextEditor::View*); - private slots: +private slots: void viewDestroyed(QObject* view); void viewCreated(KTextEditor::Document*, KTextEditor::View*); void documentCreated( KDevelop::IDocument* document ); - private: +private: void addViewInternal(KTextEditor::View* view); QList<KTextEditor::View*> m_views; - QWidget* m_childrenOf; }; -class ContextBrowserView; +class ContextBrowserPlugin; /** * Integrates the context-browser with the editor views, by listening for navigation events, and implementing html-like source browsing @@ -70,10 +69,8 @@ class ContextBrowserView; class BrowseManager : public QObject { Q_OBJECT public: - BrowseManager(ContextBrowserView* controller); + BrowseManager(ContextBrowserPlugin* controller); Q_SIGNALS: - //Emitted whenever the shift-key has been pressed + released without any other key in between - void shiftKeyTriggered(); //Emitted when browsing was started using the magic-modifier void startDelayedBrowsing(KTextEditor::View* view); void stopDelayedBrowsing(); @@ -86,10 +83,10 @@ class BrowseManager : public QObject { void viewAdded(KTextEditor::View* view); class Watcher : public EditorViewWatcher { public: - Watcher(BrowseManager* manager); - virtual void viewAdded(KTextEditor::View*); + Watcher(BrowseManager* manager); + virtual void viewAdded(KTextEditor::View*); private: - BrowseManager* m_manager; + BrowseManager* m_manager; }; void resetChangedCursor(); @@ -98,7 +95,7 @@ class BrowseManager : public QObject { //Installs/uninstalls the event-filter void applyEventFilter(QWidget* object, bool install); virtual bool eventFilter(QObject * watched, QEvent * event) ; - ContextBrowserView* m_view; + ContextBrowserPlugin* m_plugin; bool m_browsing; int m_browsingByKey; //Whether the browsing was started because of a key Watcher m_watcher; diff --git a/plugins/contextbrowser/contextbrowser.cpp b/plugins/contextbrowser/contextbrowser.cpp index f758748..cec1ae1 100644 --- a/plugins/contextbrowser/contextbrowser.cpp +++ b/plugins/contextbrowser/contextbrowser.cpp @@ -21,6 +21,9 @@ #include "contextbrowser.h" +#include "browsemanager.h" + +///TODO: cleanup includes #include <QTimer> #include <QApplication> #include <qalgorithms.h> @@ -63,9 +66,14 @@ #include <interfaces/iplugincontroller.h> #include <sublime/mainwindow.h> #include <memory> +#include <QToolButton> +#include <QLayout> +#include <language/duchain/types/functiontype.h> +#include <ktexteditor/codecompletioninterface.h> static const unsigned int highlightingTimeout = 150; static const float highlightingZDepth = -5000; +static const int maxHistoryLength = 30; using KDevelop::ILanguage; using KTextEditor::Attribute; @@ -84,11 +92,23 @@ QWidget* masterWidget(QWidget* w) { DUContext* contextForHighlightingAt(const SimpleCursor& position, TopDUContext* topContext) { DUContext* ctx = topContext->findContextAt(topContext->transformToLocalRevision(position)); - while(ctx && (ctx->type() == DUContext::Template || ctx->type() == DUContext::Helper || ctx->localScopeIdentifier().isEmpty()) && ctx->parentContext()) + while(ctx && ctx->parentContext() + && (ctx->type() == DUContext::Template || ctx->type() == DUContext::Helper + || ctx->localScopeIdentifier().isEmpty())) + { ctx = ctx->parentContext(); + } return ctx; } +///Duchain must be locked +DUContext* getContextAt(KUrl url, KTextEditor::Cursor cursor) { + TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); + if (!topContext) return 0; + return contextForHighlightingAt(SimpleCursor(cursor), topContext); +} + + class ContextBrowserViewFactory: public KDevelop::IToolViewFactory { public: @@ -97,8 +117,6 @@ public: virtual QWidget* create(QWidget *parent = 0) { ContextBrowserView* ret = new ContextBrowserView(m_plugin, parent); - QObject::connect(ret, SIGNAL(startDelayedBrowsing(KTextEditor::View*)), m_plugin, SLOT(startDelayedBrowsing(KTextEditor::View*))); - QObject::connect(ret, SIGNAL(stopDelayedBrowsing()), m_plugin, SLOT(stopDelayedBrowsing())); return ret; } @@ -115,8 +133,84 @@ public: private: ContextBrowserPlugin *m_plugin; }; +KXMLGUIClient* ContextBrowserPlugin::createGUIForMainWindow( Sublime::MainWindow* window ) +{ + KXMLGUIClient* ret = KDevelop::IPlugin::createGUIForMainWindow( window ); + + m_browseManager = new BrowseManager(this); + + connect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor))); + + m_previousButton = new QToolButton(); + m_previousButton->setToolTip(i18n("Go back in context history")); + m_previousButton->setPopupMode(QToolButton::MenuButtonPopup); + m_previousButton->setIcon(KIcon("go-previous")); + m_previousButton->setEnabled(false); + m_previousButton->setFocusPolicy(Qt::NoFocus); + m_previousMenu = new QMenu(); + m_previousButton->setMenu(m_previousMenu); + connect(m_previousButton, SIGNAL(clicked(bool)), this, SLOT(historyPrevious())); + connect(m_previousMenu, SIGNAL(aboutToShow()), this, SLOT(previousMenuAboutToShow())); + + m_nextButton = new QToolButton(); + m_nextButton->setToolTip(i18n("Go forward in context history")); + m_nextButton->setPopupMode(QToolButton::MenuButtonPopup); + m_nextButton->setIcon(KIcon("go-next")); + m_nextButton->setEnabled(false); + m_nextButton->setFocusPolicy(Qt::NoFocus); + m_nextMenu = new QMenu(); + m_nextButton->setMenu(m_nextMenu); + connect(m_nextButton, SIGNAL(clicked(bool)), this, SLOT(historyNext())); + connect(m_nextMenu, SIGNAL(aboutToShow()), this, SLOT(nextMenuAboutToShow())); + + m_browseButton = new QToolButton(); + m_browseButton->setIcon(KIcon("games-hint")); + m_browseButton->setToolTip(i18n("Enable/disable source browse mode")); + m_browseButton->setWhatsThis(i18n("When this is enabled, you can browse the source-code by clicking in the editor.")); + m_browseButton->setCheckable(true); + m_browseButton->setFocusPolicy(Qt::NoFocus); + + connect(m_browseButton, SIGNAL(clicked(bool)), m_browseManager, SLOT(setBrowsing(bool))); + + IQuickOpen* quickOpen = KDevelop::ICore::self()->pluginController()->extensionForPlugin<IQuickOpen>("org.kdevelop.IQuickOpen"); + + if(quickOpen) { + m_outlineLine = quickOpen->createQuickOpenLine(QStringList(), QStringList() << i18n("Outline"), IQuickOpen::Outline); + m_outlineLine->setDefaultText(i18n("Outline...")); + m_outlineLine->setToolTip(i18n("Navigate outline of active document, click to browse.")); + } + + connect(m_browseManager, SIGNAL(startDelayedBrowsing(KTextEditor::View*)), + this, SLOT(startDelayedBrowsing(KTextEditor::View*))); + connect(m_browseManager, SIGNAL(stopDelayedBrowsing()), + this, SLOT(stopDelayedBrowsing())); + + m_toolbarWidget = toolbarWidgetForMainWindow(window); + m_toolbarWidgetLayout = new QHBoxLayout; + m_toolbarWidgetLayout->setSizeConstraint(QLayout::SetMaximumSize); + m_previousButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_nextButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_browseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_toolbarWidgetLayout->setMargin(0); + + m_toolbarWidgetLayout->addWidget(m_previousButton); + m_toolbarWidgetLayout->addWidget(m_outlineLine); + m_outlineLine->setMaximumWidth(600); + m_toolbarWidgetLayout->addWidget(m_nextButton); + m_toolbarWidgetLayout->addWidget(m_browseButton); + + if(m_toolbarWidget->children().isEmpty()) + m_toolbarWidget->setLayout(m_toolbarWidgetLayout); + + connect(ICore::self()->documentController(), SIGNAL(documentClosed(KDevelop::IDocument*)), m_outlineLine, SLOT(clear())); + connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), m_outlineLine, SLOT(clear())); -void ContextBrowserPlugin::createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions) { + return ret; +} + +void ContextBrowserPlugin::createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, + KActionCollection& actions) +{ xmlFile = "kdevcontextbrowser.rc" ; KAction* previousContext = actions.addAction("previous_context"); @@ -150,28 +244,16 @@ void ContextBrowserPlugin::createActionsForMainWindow(Sublime::MainWindow* windo outline->setDefaultWidget(w); } - void ContextBrowserPlugin::nextContextShortcut() { - foreach(ContextBrowserView* view, m_views) - { - kDebug() << masterWidget(ICore::self()->uiController()->activeMainWindow()) << masterWidget(view); - if(masterWidget(ICore::self()->uiController()->activeMainWindow()) == masterWidget(view)) { - view->historyNext(); - return; - } - } + // TODO: cleanup + historyNext(); } void ContextBrowserPlugin::previousContextShortcut() { - foreach(ContextBrowserView* view, m_views) - { - if(masterWidget(ICore::self()->uiController()->activeMainWindow()) == masterWidget(view)) { - view->historyPrevious(); - return; - } - } + // TODO: cleanup + historyPrevious(); } K_PLUGIN_FACTORY(ContextBrowserFactory, registerPlugin<ContextBrowserPlugin>(); ) @@ -179,7 +261,8 @@ K_EXPORT_PLUGIN(ContextBrowserFactory(KAboutData("kdevcontextbrowser","kdevconte ContextBrowserPlugin::ContextBrowserPlugin(QObject *parent, const QVariantList&) : KDevelop::IPlugin(ContextBrowserFactory::componentData(), parent) - , m_viewFactory(new ContextBrowserViewFactory(this)), m_lastInsertionDocument(0) + , m_viewFactory(new ContextBrowserViewFactory(this)) + , m_nextHistoryIndex(0) { core()->uiController()->addToolView(i18n("Code Browser"), m_viewFactory); @@ -200,6 +283,15 @@ ContextBrowserPlugin::ContextBrowserPlugin(QObject *parent, const QVariantList&) ContextBrowserPlugin::~ContextBrowserPlugin() { + ///TODO: QObject inheritance should suffice? + delete m_nextMenu; + delete m_previousMenu; + delete m_toolbarWidgetLayout; + + delete m_previousButton; + delete m_outlineLine; + delete m_nextButton; + delete m_browseButton; } void ContextBrowserPlugin::unload() @@ -269,6 +361,7 @@ void ContextBrowserPlugin::stopDelayedBrowsing() { if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; + m_currentNavigationWidget = 0; } } @@ -282,6 +375,7 @@ void ContextBrowserPlugin::hideTooTip() { if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; + m_currentNavigationWidget = 0; } } @@ -330,12 +424,14 @@ void ContextBrowserPlugin::showToolTip(KTextEditor::View* view, KTextEditor::Cur if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; + m_currentNavigationWidget = 0; } KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, view->mapToGlobal(view->cursorToCoordinate(position)) + QPoint(20, 40), navigationWidget); tooltip->resize( navigationWidget->sizeHint() + QSize(10, 10) ); kDebug() << "tooltip size" << tooltip->size(); m_currentToolTip = tooltip; + m_currentNavigationWidget = navigationWidget; ActiveToolTip::showToolTip(tooltip); //First disconnect to prevent multiple connections @@ -529,9 +625,7 @@ void ContextBrowserPlugin::updateForView(View* view) //Only update the history if this context is around the text cursor if(core()->documentController()->activeDocument() && highlightPosition == SimpleCursor(view->cursorPosition()) && view->document() == core()->documentController()->activeDocument()->textDocument()) { - foreach(ContextBrowserView* contextView, m_views) - if(masterWidget(contextView) == masterWidget(view)) - contextView->updateHistory(ctx, highlightPosition); + updateHistory(ctx, highlightPosition); } Declaration* foundDeclaration = findDeclaration(view, highlightPosition, mouseHighlight); @@ -868,18 +962,340 @@ void ContextBrowserPlugin::unRegisterToolView(ContextBrowserView* view) m_views.removeAll(view); } -QWidget* ContextBrowserPlugin::toolbarWidgetForMainWindow(QWidget* widgetInWindow) +// history browsing + +QWidget* ContextBrowserPlugin::toolbarWidgetForMainWindow( Sublime::MainWindow* window ) { - QWidget* master = masterWidget(widgetInWindow); - - for(QList< QPointer< QWidget > >::const_iterator it = m_toolbarWidgets.constBegin(); it != m_toolbarWidgets.constEnd(); ++it) { - if((*it) && masterWidget(*it) == master) { - return *it; + //TODO: support multiple windows (if that ever gets revived) + if (!m_toolbarWidget) { + m_toolbarWidget = new QWidget(window); + } + return m_toolbarWidget; +} + +void ContextBrowserPlugin::documentJumpPerformed( KDevelop::IDocument* newDocument, + const KTextEditor::Cursor& newCursor, + KDevelop::IDocument* previousDocument, + const KTextEditor::Cursor& previousCursor) { + + DUChainReadLocker lock(DUChain::lock()); + + /*TODO: support multiple windows if that ever gets revived + if(newDocument && newDocument->textDocument() && newDocument->textDocument()->activeView() && masterWidget(newDocument->textDocument()->activeView()) != masterWidget(this)) + return; + */ + + if(previousDocument && previousCursor.isValid()) { + kDebug() << "updating jump source"; + DUContext* context = getContextAt(previousDocument->url(), previousCursor); + if(context) { + updateHistory(context, SimpleCursor(previousCursor), true); + }else{ + //We just want this place in the history + m_history.resize(m_nextHistoryIndex); // discard forward history + m_history.append(HistoryEntry(DocumentCursor(IndexedString(previousDocument->url()), SimpleCursor(previousCursor)))); + ++m_nextHistoryIndex; + } + } + kDebug() << "new doc: " << newDocument << " new cursor: " << newCursor; + if(newDocument && newCursor.isValid()) { + kDebug() << "updating jump target"; + DUContext* context = getContextAt(newDocument->url(), newCursor); + if(context) { + updateHistory(context, SimpleCursor(newCursor), true); + }else{ + //We just want this place in the history + m_history.resize(m_nextHistoryIndex); // discard forward history + m_history.append(HistoryEntry(DocumentCursor(IndexedString(newDocument->url()), SimpleCursor(newCursor)))); + ++m_nextHistoryIndex; + m_outlineLine->clear(); + } + } +} + +void ContextBrowserPlugin::updateButtonState() +{ + m_nextButton->setEnabled( m_nextHistoryIndex < m_history.size() ); + m_previousButton->setEnabled( m_nextHistoryIndex >= 2 ); +} + +void ContextBrowserPlugin::historyNext() { + if(m_nextHistoryIndex >= m_history.size()) { + return; + } + openDocument(m_nextHistoryIndex); // opening the document at given position + // will update the widget for us + ++m_nextHistoryIndex; + updateButtonState(); +} + +void ContextBrowserPlugin::openDocument(int historyIndex) { + Q_ASSERT_X(historyIndex >= 0, "openDocument", "negative history index"); + Q_ASSERT_X(historyIndex < m_history.size(), "openDocument", "history index out of range"); + DocumentCursor c = m_history[historyIndex].computePosition(); + if (c.isValid() && !c.document.str().isEmpty()) { + + disconnect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor))); + + ICore::self()->documentController()->openDocument(c.document.toUrl(), c.textCursor()); + + connect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor))); + + KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); + updateDeclarationListBox(m_history[historyIndex].context.data()); } +} + +void ContextBrowserPlugin::historyPrevious() { + if(m_nextHistoryIndex < 2) { + return; + } + --m_nextHistoryIndex; + openDocument(m_nextHistoryIndex-1); // opening the document at given position + // will update the widget for us + updateButtonState(); +} + +QString ContextBrowserPlugin::actionTextFor(int historyIndex) const +{ + const HistoryEntry& entry = m_history.at(historyIndex); + QString actionText = entry.context.data() ? entry.context.data()->scopeIdentifier(true).toString() : QString(); + if(actionText.isEmpty()) + actionText = entry.alternativeString; + if(actionText.isEmpty()) + actionText = "<unnamed>"; + actionText += " @ "; + QString fileName = entry.absoluteCursorPosition.document.toUrl().fileName(); + actionText += QString("%1:%2").arg(fileName).arg(entry.absoluteCursorPosition.line+1); + return actionText; +} + +/* +inline QDebug operator<<(QDebug debug, const ContextBrowserPlugin::HistoryEntry &he) +{ + DocumentCursor c = he.computePosition(); + debug << "\n\tHistoryEntry " << c.line << " " << c.document.str(); + return debug; +} +*/ + +void ContextBrowserPlugin::nextMenuAboutToShow() { + QList<int> indices; + for(int a = m_nextHistoryIndex; a < m_history.size(); ++a) { + indices << a; + } + fillHistoryPopup(m_nextMenu, indices); +} + +void ContextBrowserPlugin::previousMenuAboutToShow() { + QList<int> indices; + for(int a = m_nextHistoryIndex-2; a >= 0; --a) { + indices << a; + } + fillHistoryPopup(m_previousMenu, indices); +} + +void ContextBrowserPlugin::fillHistoryPopup(QMenu* menu, const QList<int>& historyIndices) { + menu->clear(); + KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); + foreach(int index, historyIndices) { + QAction* action = new QAction(actionTextFor(index), menu); + action->setData(index); + menu->addAction(action); + connect(action, SIGNAL(triggered(bool)), this, SLOT(actionTriggered())); + } +} + +bool ContextBrowserPlugin::isPreviousEntry(KDevelop::DUContext* context, + const KDevelop::SimpleCursor& /*position*/) const +{ + if (m_nextHistoryIndex == 0) return false; + Q_ASSERT(m_nextHistoryIndex <= m_history.count()); + const HistoryEntry& he = m_history.at(m_nextHistoryIndex-1); + KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); // is this necessary?? + Q_ASSERT(context); + return IndexedDUContext(context) == he.context; +} + +void ContextBrowserPlugin::updateHistory(KDevelop::DUContext* context, const KDevelop::SimpleCursor& position, bool force) +{ + kDebug() << "updating history"; + + if(m_outlineLine->isVisible()) + updateDeclarationListBox(context); + + if(!context && !context->owner() && !force) { + return; //Only add history-entries for contexts that have owners, which in practice should be functions and classes + //This keeps the history cleaner + } + + if (isPreviousEntry(context, position)) { + if(m_nextHistoryIndex) { + HistoryEntry& he = m_history[m_nextHistoryIndex-1]; + he.setCursorPosition(position); + } + return; + } else { // Append new history entry + m_history.resize(m_nextHistoryIndex); // discard forward history + m_history.append(HistoryEntry(IndexedDUContext(context), position)); + ++m_nextHistoryIndex; + + updateButtonState(); + if(m_history.size() > (maxHistoryLength + 5)) { + m_history = m_history.mid(m_history.size() - maxHistoryLength); + m_nextHistoryIndex = m_history.size(); + } + } +} + +void ContextBrowserPlugin::setAllowBrowsing(bool allow) { + m_browseButton->setChecked(allow); +} + +void ContextBrowserPlugin::updateDeclarationListBox(DUContext* context) { + if(!context || !context->owner()) { + kDebug() << "not updating box"; + m_listUrl = IndexedString(); ///@todo Compute the context in the document here + m_outlineLine->clear(); + return; + } + + Declaration* decl = context->owner(); + + m_listUrl = context->url(); + + Declaration* specialDecl = SpecializationStore::self().applySpecialization(decl, decl->topContext()); + + FunctionType::Ptr function = specialDecl->type<FunctionType>(); + QString text = specialDecl->qualifiedIdentifier().toString(); + if(function) + text += function->partToString(KDevelop::FunctionType::SignatureArguments); + + if(!m_outlineLine->hasFocus()) + { + m_outlineLine->setText(text); + m_outlineLine->setCursorPosition(0); + } + + kDebug() << "updated" << text; +} + +void ContextBrowserPlugin::actionTriggered() { + QAction* action = qobject_cast<QAction*>(sender()); + Q_ASSERT(action); Q_ASSERT(action->data().type() == QVariant::Int); + int historyPosition = action->data().toInt(); + // kDebug() << "history pos" << historyPosition << m_history.size() << m_history; + if(historyPosition >= 0 && historyPosition < m_history.size()) { + m_nextHistoryIndex = historyPosition + 1; + openDocument(historyPosition); + updateButtonState(); + } +} + +void ContextBrowserPlugin::doNavigate(NavigationActionType action) +{ + ///TODO: is this *really* required? + KTextEditor::View* view = qobject_cast<KTextEditor::View*>(sender()); + if(!view) { + kWarning() << "sender is not a view"; + return; } - m_toolbarWidgets.append(new QWidget(master)); - m_toolbarWidgets.back()->setHidden(true); - return m_toolbarWidgets.back(); + KTextEditor::CodeCompletionInterface* iface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); + if(!iface || iface->isCompletionActive()) + return; + /// + + QList<QWidget*> widgets; + widgets << m_currentNavigationWidget.data(); + foreach(ContextBrowserView* view, m_views) { + widgets << view->navigationWidget(); + } + + foreach(QWidget* w, widgets) { + AbstractNavigationWidget* widget = qobject_cast<AbstractNavigationWidget*>(w); + if (!widget) + continue; + + switch(action) { + case Accept: + widget->accept(); + break; + case Back: + widget->back(); + break; + case Left: + widget->previous(); + break; + case Right: + widget->next(); + break; + case Up: + widget->up(); + break; + case Down: + widget->down(); + break; + } + } +} + +void ContextBrowserPlugin::navigateAccept() { + doNavigate(Accept); +} + +void ContextBrowserPlugin::navigateBack() { + doNavigate(Back); +} + +void ContextBrowserPlugin::navigateDown() { + doNavigate(Down); +} + +void ContextBrowserPlugin::navigateLeft() { + doNavigate(Left); +} + +void ContextBrowserPlugin::navigateRight() { + doNavigate(Right); +} + +void ContextBrowserPlugin::navigateUp() { + doNavigate(Up); +} + + +//BEGIN HistoryEntry +ContextBrowserPlugin::HistoryEntry::HistoryEntry(KDevelop::DocumentCursor pos) : absoluteCursorPosition(pos) { +} + +ContextBrowserPlugin::HistoryEntry::HistoryEntry(IndexedDUContext ctx, const KDevelop::SimpleCursor& cursorPosition) : context(ctx) { + //Use a position relative to the context + setCursorPosition(cursorPosition); + if(ctx.data()) + alternativeString = ctx.data()->scopeIdentifier(true).toString(); + if(!alternativeString.isEmpty()) + alternativeString += i18n("(changed)"); //This is used when the context was deleted in between +} + +DocumentCursor ContextBrowserPlugin::HistoryEntry::computePosition() const { + KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); + DocumentCursor ret; + if(context.data()) { + ret = DocumentCursor(context.data()->url(), relativeCursorPosition); + ret.line += context.data()->range().start.line; + }else{ + ret = absoluteCursorPosition; + } + return ret; +} + +void ContextBrowserPlugin::HistoryEntry::setCursorPosition(const KDevelop::SimpleCursor& cursorPosition) { + KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); + if(context.data()) { + absoluteCursorPosition = DocumentCursor(context.data()->url(), cursorPosition); + relativeCursorPosition = cursorPosition; + relativeCursorPosition.line -= context.data()->range().start.line; + } } #include "contextbrowser.moc" diff --git a/plugins/contextbrowser/contextbrowser.h b/plugins/contextbrowser/contextbrowser.h index 7b9262e..22893ce 100644 --- a/plugins/contextbrowser/contextbrowser.h +++ b/plugins/contextbrowser/contextbrowser.h @@ -34,6 +34,16 @@ #include <language/duchain/declaration.h> #include <KUrl> #include <language/editor/persistentmovingrange.h> +#include <language/interfaces/iquickopen.h> +#include <QToolButton> +#include <QMenu> +#include <QHBoxLayout> +#include <language/editor/documentcursor.h> +#include <KTextEditor/Document> + +namespace Sublime { + class MainWindow; +} namespace KDevelop { class IDocument; @@ -48,10 +58,12 @@ namespace KTextEditor { class View; } +//TODO: move into kdevelop namespace using namespace KDevelop; class ContextBrowserViewFactory; class ContextBrowserView; +class BrowseManager; QWidget* masterWidget(QWidget* w); @@ -81,8 +93,16 @@ class ContextBrowserPlugin : public KDevelop::IPlugin virtual KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context*); - QWidget* toolbarWidgetForMainWindow(QWidget* widgetInWindow); - + virtual KXMLGUIClient* createGUIForMainWindow( Sublime::MainWindow* window ); + + ///duchain must be locked + ///@param force When this is true, the history-entry is added, no matter whether the context is "interesting" or not + void updateHistory(KDevelop::DUContext* context, const KDevelop::SimpleCursor& cursorPosition, + bool force = false); + + void updateDeclarationListBox(KDevelop::DUContext* context); + void setAllowBrowsing(bool allow); + public Q_SLOTS: void previousContextShortcut(); void nextContextShortcut(); @@ -110,10 +130,31 @@ class ContextBrowserPlugin : public KDevelop::IPlugin void textInserted(KTextEditor::Document*, KTextEditor::Range); void selectionChanged(KTextEditor::View*); - + + private slots: + // history browsing + void documentJumpPerformed( KDevelop::IDocument* newDocument, + const KTextEditor::Cursor& newCursor, + KDevelop::IDocument* previousDocument, + const KTextEditor::Cursor& previousCursor); + + void historyNext(); + void historyPrevious(); + void nextMenuAboutToShow(); + void previousMenuAboutToShow(); + void actionTriggered(); + + void navigateLeft(); + void navigateRight(); + void navigateUp(); + void navigateDown(); + void navigateAccept(); + void navigateBack(); + private: - - virtual void createActionsForMainWindow(Sublime::MainWindow* /*window*/, QString& xmlFile, KActionCollection& actions); + QWidget* toolbarWidgetForMainWindow(Sublime::MainWindow* window); + virtual void createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, + KActionCollection& actions); void switchUse(bool forward); void clearMouseHover(); @@ -125,8 +166,24 @@ class ContextBrowserPlugin : public KDevelop::IPlugin KDevelop::Declaration* findDeclaration(KTextEditor::View* view, const KDevelop::SimpleCursor&, bool mouseHighlight); void updateForView(KTextEditor::View* view); + // history browsing + bool isPreviousEntry(KDevelop::DUContext*, const KDevelop::SimpleCursor& cursor) const; + QString actionTextFor(int historyIndex) const; + void updateButtonState(); + void openDocument(int historyIndex); + void fillHistoryPopup(QMenu* menu, const QList<int>& historyIndices); + + enum NavigationActionType { + Accept, + Back, + Down, + Up, + Left, + Right + }; + void doNavigate(NavigationActionType action); + private: - ContextBrowserView* browserViewForTextView(KTextEditor::View* view); void showToolTip(KTextEditor::View* view, KTextEditor::Cursor position); @@ -147,12 +204,46 @@ class ContextBrowserPlugin : public KDevelop::IPlugin SimpleCursor m_mouseHoverCursor; ContextBrowserViewFactory* m_viewFactory; QPointer<QWidget> m_currentToolTip; + QPointer<QWidget> m_currentNavigationWidget; IndexedDeclaration m_currentToolTipDeclaration; QAction* m_findUses; QPointer<KTextEditor::Document> m_lastInsertionDocument; KTextEditor::Cursor m_lastInsertionPos; - QList<QPointer<QWidget> > m_toolbarWidgets; + + // outline toolbar + QPointer<KDevelop::IQuickOpenLine> m_outlineLine; + QPointer<QHBoxLayout> m_toolbarWidgetLayout; + QPointer<QWidget> m_toolbarWidget; + + // history browsing + struct HistoryEntry { + //Duchain must be locked + HistoryEntry(KDevelop::IndexedDUContext ctx = KDevelop::IndexedDUContext(), const KDevelop::SimpleCursor& cursorPosition = KDevelop::SimpleCursor()); + HistoryEntry(KDevelop::DocumentCursor pos); + //Duchain must be locked + void setCursorPosition(const KDevelop::SimpleCursor& cursorPosition); + + //Duchain does not need to be locked + KDevelop::DocumentCursor computePosition() const; + + KDevelop::IndexedDUContext context; + KDevelop::DocumentCursor absoluteCursorPosition; + KDevelop::SimpleCursor relativeCursorPosition; //Cursor position relative to the start line of the context + QString alternativeString; + }; + + QVector<HistoryEntry> m_history; + QPointer<QToolButton> m_previousButton; + QPointer<QToolButton> m_nextButton; + QPointer<QMenu> m_previousMenu, m_nextMenu; + QPointer<QToolButton> m_browseButton; + QList<KDevelop::IndexedDeclaration> m_listDeclarations; + KDevelop::IndexedString m_listUrl; + BrowseManager* m_browseManager; + //Used to not record jumps triggered by the context-browser as history entries + QPointer<QWidget> m_focusBackWidget; + int m_nextHistoryIndex; }; #endif // CONTEXTBROWSERPLUGIN_H diff --git a/plugins/contextbrowser/contextbrowserview.cpp b/plugins/contextbrowser/contextbrowserview.cpp index 0c04384..2e4f2d8 100644 --- a/plugins/contextbrowser/contextbrowserview.cpp +++ b/plugins/contextbrowser/contextbrowserview.cpp @@ -20,6 +20,8 @@ */ #include "contextbrowserview.h" + +///TODO: cleanup includes #include <QVBoxLayout> #include <QHBoxLayout> #include <QLabel> @@ -56,341 +58,9 @@ #include <language/duchain/navigation/abstractdeclarationnavigationcontext.h> #include <interfaces/contextmenuextension.h> #include <interfaces/iplugincontroller.h> -#include <ktexteditor/codecompletioninterface.h> -#include <language/interfaces/iquickopen.h> - -const int maxHistoryLength = 30; using namespace KDevelop; -DUContext* contextAt(const CursorInRevision& position, TopDUContext* topContext) -{ - DUContext* ctx = topContext->findContextAt(position); - while(ctx && (ctx->type() == DUContext::Template || ctx->type() == DUContext::Helper || ctx->localScopeIdentifier().isEmpty()) && ctx->parentContext()) - ctx = ctx->parentContext(); - return ctx; -} - -///Duchain must be locked -DUContext* getContextAt(KUrl url, KTextEditor::Cursor cursor) { - TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); - if (!topContext) return 0; - return contextAt(topContext->transformToLocalRevision(SimpleCursor(cursor)), topContext); -} - -static bool useNavigationFromView(QObject* viewObject) { - KTextEditor::View* view = qobject_cast<KTextEditor::View*>(viewObject); - if(!view) { - kWarning() << "sender is not a view"; - return false; - } - KTextEditor::CodeCompletionInterface* iface = dynamic_cast<KTextEditor::CodeCompletionInterface*>(view); - if(!iface || iface->isCompletionActive()) - return false; - - return true; -} - -void ContextBrowserView::navigateAccept() { - if(!useNavigationFromView(sender())) - return; - - if(AbstractNavigationWidget* widget = dynamic_cast<AbstractNavigationWidget*>(m_navigationWidget.data())) - widget->accept(); -} - -void ContextBrowserView::navigateBack() { - if(!useNavigationFromView(sender())) - return; - - if(AbstractNavigationWidget* widget = dynamic_cast<AbstractNavigationWidget*>(m_navigationWidget.data())) - widget->back(); -} - -void ContextBrowserView::navigateDown() { - if(!useNavigationFromView(sender())) - return; - - if(AbstractNavigationWidget* widget = dynamic_cast<AbstractNavigationWidget*>(m_navigationWidget.data())) - widget->down(); -} - -void ContextBrowserView::navigateLeft() { - if(!useNavigationFromView(sender())) - return; - - if(AbstractNavigationWidget* widget = dynamic_cast<AbstractNavigationWidget*>(m_navigationWidget.data())) - widget->previous(); -} - -void ContextBrowserView::navigateRight() { - if(!useNavigationFromView(sender())) - return; - - if(AbstractNavigationWidget* widget = dynamic_cast<AbstractNavigationWidget*>(m_navigationWidget.data())) - widget->next(); -} - -void ContextBrowserView::navigateUp() { - if(!useNavigationFromView(sender())) - return; - - if(AbstractNavigationWidget* widget = dynamic_cast<AbstractNavigationWidget*>(m_navigationWidget.data())) - widget->up(); -} - -void ContextBrowserView::documentJumpPerformed( KDevelop::IDocument* newDocument, KTextEditor::Cursor newCursor, KDevelop::IDocument* previousDocument, KTextEditor::Cursor previousCursor) { - - DUChainReadLocker lock(DUChain::lock()); - - if(newDocument && newDocument->textDocument() && newDocument->textDocument()->activeView() && masterWidget(newDocument->textDocument()->activeView()) != masterWidget(this)) - return; - - if(previousDocument && previousCursor.isValid()) { - kDebug() << "updating jump source"; - DUContext* context = getContextAt(previousDocument->url(), previousCursor); - if(context) { - updateHistory(context, SimpleCursor(previousCursor), true); - }else{ - //We just want this place in the history - m_history.resize(m_nextHistoryIndex); // discard forward history - m_history.append(HistoryEntry(DocumentCursor(IndexedString(previousDocument->url()), SimpleCursor(previousCursor)))); - ++m_nextHistoryIndex; - } - } - kDebug() << "new doc: " << newDocument << " new cursor: " << newCursor; - if(newDocument && newCursor.isValid()) { - kDebug() << "updating jump target"; - DUContext* context = getContextAt(newDocument->url(), newCursor); - if(context) { - updateHistory(context, SimpleCursor(newCursor), true); - }else{ - //We just want this place in the history - m_history.resize(m_nextHistoryIndex); // discard forward history - m_history.append(HistoryEntry(DocumentCursor(IndexedString(newDocument->url()), SimpleCursor(newCursor)))); - ++m_nextHistoryIndex; - m_outlineLine->clear(); - } - } -} - -void ContextBrowserView::updateButtonState() -{ - m_nextButton->setEnabled( m_nextHistoryIndex < m_history.size() ); - m_previousButton->setEnabled( m_nextHistoryIndex >= 2 ); -} - -void ContextBrowserView::historyNext() { - if(m_nextHistoryIndex >= m_history.size()) { - return; - } - allowLockedUpdate(); - openDocument(m_nextHistoryIndex); // opening the document at given position - // will update the widget for us - ++m_nextHistoryIndex; - updateButtonState(); -} - -void ContextBrowserView::openDocument(int historyIndex) { - Q_ASSERT_X(historyIndex >= 0, "openDocument", "negative history index"); - Q_ASSERT_X(historyIndex < m_history.size(), "openDocument", "history index out of range"); - DocumentCursor c = m_history[historyIndex].computePosition(); - if (c.isValid() && !c.document.str().isEmpty()) { - - disconnect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor))); - - ICore::self()->documentController()->openDocument(c.document.toUrl(), c.textCursor()); - - connect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor))); - - KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); - updateDeclarationListBox(m_history[historyIndex].context.data()); - } -} - -void ContextBrowserView::historyPrevious() { - if(m_nextHistoryIndex < 2) { - return; - } - --m_nextHistoryIndex; - allowLockedUpdate(); - openDocument(m_nextHistoryIndex-1); // opening the document at given position - // will update the widget for us - updateButtonState(); -} - -QString ContextBrowserView::actionTextFor(int historyIndex) -{ - HistoryEntry& entry = m_history[historyIndex]; - QString actionText = entry.context.data() ? entry.context.data()->scopeIdentifier(true).toString() : QString(); - if(actionText.isEmpty()) - actionText = entry.alternativeString; - if(actionText.isEmpty()) - actionText = "<unnamed>"; - actionText += " @ "; - QString fileName = entry.absoluteCursorPosition.document.toUrl().fileName(); - actionText += QString("%1:%2").arg(fileName).arg(entry.absoluteCursorPosition.line+1); - return actionText; -} - -inline QDebug operator<<(QDebug debug, const ContextBrowserView::HistoryEntry &he) -{ - DocumentCursor c = he.computePosition(); - debug << "\n\tHistoryEntry " << c.line << " " << c.document.str(); - return debug; -} - -void ContextBrowserView::nextMenuAboutToShow() { - QList<int> indices; - for(int a = m_nextHistoryIndex; a < m_history.size(); ++a) { - indices << a; - } - fillHistoryPopup(m_nextMenu, indices); -} - -void ContextBrowserView::previousMenuAboutToShow() { - QList<int> indices; - for(int a = m_nextHistoryIndex-2; a >= 0; --a) { - indices << a; - } - fillHistoryPopup(m_previousMenu, indices); -} - -void ContextBrowserView::fillHistoryPopup(QMenu* menu, const QList<int>& historyIndices) { - menu->clear(); - KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); - foreach(int index, historyIndices) { - QAction* action = new QAction(actionTextFor(index), menu); - action->setData(index); - menu->addAction(action); - connect(action, SIGNAL(triggered(bool)), this, SLOT(actionTriggered())); - } -} - -void ContextBrowserView::switchFocusToContextBrowser() { - if(isVisible()) { - kDebug() << "switching focus to context-browser"; - if(QApplication::focusWidget() != this) - m_focusBackWidget = QApplication::focusWidget(); - setFocus(); - } -} - -void ContextBrowserView::actionTriggered() { - QAction* action = qobject_cast<QAction*>(sender()); - Q_ASSERT(action); Q_ASSERT(action->data().type() == QVariant::Int); - int historyPosition = action->data().toInt(); - // kDebug() << "history pos" << historyPosition << m_history.size() << m_history; - if(historyPosition >= 0 && historyPosition < m_history.size()) { - m_nextHistoryIndex = historyPosition + 1; - allowLockedUpdate(); // opening the document at given position - // will update the widget for us - openDocument(historyPosition); - updateButtonState(); - } -} - -ContextBrowserView::HistoryEntry::HistoryEntry(KDevelop::DocumentCursor pos) : absoluteCursorPosition(pos) { -} - -ContextBrowserView::HistoryEntry::HistoryEntry(IndexedDUContext ctx, const KDevelop::SimpleCursor& cursorPosition) : context(ctx) { - //Use a position relative to the context - setCursorPosition(cursorPosition); - if(ctx.data()) - alternativeString = ctx.data()->scopeIdentifier(true).toString(); - if(!alternativeString.isEmpty()) - alternativeString += i18n("(changed)"); //This is used when the context was deleted in between -} - -DocumentCursor ContextBrowserView::HistoryEntry::computePosition() const { - KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); - DocumentCursor ret; - if(context.data()) { - ret = DocumentCursor(context.data()->url(), relativeCursorPosition); - ret.line += context.data()->range().start.line; - }else{ - ret = absoluteCursorPosition; - } - return ret; -} - -void ContextBrowserView::HistoryEntry::setCursorPosition(const KDevelop::SimpleCursor& cursorPosition) { - KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); - if(context.data()) { - absoluteCursorPosition = DocumentCursor(context.data()->url(), cursorPosition); - relativeCursorPosition = cursorPosition; - relativeCursorPosition.line -= context.data()->range().start.line; - } -} - -bool ContextBrowserView::isPreviousEntry(KDevelop::DUContext* context, const KDevelop::SimpleCursor& /*position*/) { - if (m_nextHistoryIndex == 0) return false; - Q_ASSERT(m_nextHistoryIndex <= m_history.count()); - HistoryEntry& he = m_history[m_nextHistoryIndex-1]; - KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); // is this necessary?? - Q_ASSERT(context); - return IndexedDUContext(context) == he.context; -} - -void ContextBrowserView::updateDeclarationListBox(DUContext* context) { - if(!context || !context->owner()) { - kDebug() << "not updating box"; - m_listUrl = IndexedString(); ///@todo Compute the context in the document here - m_outlineLine->clear(); - return; - } - - Declaration* decl = context->owner(); - - m_listUrl = context->url(); - - Declaration* specialDecl = SpecializationStore::self().applySpecialization(decl, decl->topContext()); - - FunctionType::Ptr function = specialDecl->type<FunctionType>(); - QString text = specialDecl->qualifiedIdentifier().toString(); - if(function) - text += function->partToString(KDevelop::FunctionType::SignatureArguments); - - if(!m_outlineLine->hasFocus()) - { - m_outlineLine->setText(text); - m_outlineLine->setCursorPosition(0); - } - - kDebug() << "updated" << text; -} - -void ContextBrowserView::updateHistory(KDevelop::DUContext* context, const KDevelop::SimpleCursor& position, bool force) -{ - kDebug() << "updating history"; - - if(m_outlineLine->isVisible()) - updateDeclarationListBox(context); - - if(!context && !context->owner() && !force) { - return; //Only add history-entries for contexts that have owners, which in practice should be functions and classes - //This keeps the history cleaner - } - - if (isPreviousEntry(context, position)) { - if(m_nextHistoryIndex) { - HistoryEntry& he = m_history[m_nextHistoryIndex-1]; - he.setCursorPosition(position); - } - return; - } else { // Append new history entry - m_history.resize(m_nextHistoryIndex); // discard forward history - m_history.append(HistoryEntry(IndexedDUContext(context), position)); - ++m_nextHistoryIndex; - - updateButtonState(); - if(m_history.size() > (maxHistoryLength + 5)) { - m_history = m_history.mid(m_history.size() - maxHistoryLength); - m_nextHistoryIndex = m_history.size(); - } - } -} - QWidget* ContextBrowserView::createWidget(KDevelop::DUContext* context) { m_context = IndexedDUContext(context); if(m_context.data()) { @@ -439,91 +109,24 @@ void ContextBrowserView::updateLockIcon(bool checked) { m_lockButton->setIcon(KIcon(checked ? "document-encrypt" : "document-decrypt")); } -ContextBrowserView::ContextBrowserView( ContextBrowserPlugin* plugin, QWidget* parent ) : QWidget(parent), m_plugin(plugin), m_navigationWidget(new KTextBrowser()), m_nextHistoryIndex(0), m_outlineLine(0) { +ContextBrowserView::ContextBrowserView( ContextBrowserPlugin* plugin, QWidget* parent ) : QWidget(parent), m_plugin(plugin), m_navigationWidget(new KTextBrowser()) { setWindowIcon( KIcon("applications-development-web") ); - m_browseManager = new BrowseManager(this); - - connect(ICore::self()->documentController(), SIGNAL(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor)), this, SLOT(documentJumpPerformed(KDevelop::IDocument*, KTextEditor::Cursor, KDevelop::IDocument*, KTextEditor::Cursor))); - - connect(m_browseManager, SIGNAL(shiftKeyTriggered()), this, SLOT(switchFocusToContextBrowser())); - - m_previousButton = new QToolButton(); - m_previousButton->setToolTip(i18n("Go back in context history")); - m_previousButton->setPopupMode(QToolButton::MenuButtonPopup); - m_previousButton->setIcon(KIcon("go-previous")); - m_previousButton->setEnabled(false); - m_previousButton->setFocusPolicy(Qt::NoFocus); - m_previousMenu = new QMenu(); - m_previousButton->setMenu(m_previousMenu); - connect(m_previousButton, SIGNAL(clicked(bool)), this, SLOT(historyPrevious())); - connect(m_previousMenu, SIGNAL(aboutToShow()), this, SLOT(previousMenuAboutToShow())); - - m_nextButton = new QToolButton(); - m_nextButton->setToolTip(i18n("Go forward in context history")); - m_nextButton->setPopupMode(QToolButton::MenuButtonPopup); - m_nextButton->setIcon(KIcon("go-next")); - m_nextButton->setEnabled(false); - m_nextButton->setFocusPolicy(Qt::NoFocus); - m_nextMenu = new QMenu(); - m_nextButton->setMenu(m_nextMenu); - connect(m_nextButton, SIGNAL(clicked(bool)), this, SLOT(historyNext())); - connect(m_nextMenu, SIGNAL(aboutToShow()), this, SLOT(nextMenuAboutToShow())); - - m_browseButton = new QToolButton(); - m_browseButton->setIcon(KIcon("games-hint")); - m_browseButton->setToolTip(i18n("Enable/disable source browse mode")); - m_browseButton->setWhatsThis(i18n("When this is enabled, you can browse the source-code by clicking in the editor.")); - m_browseButton->setCheckable(true); - m_browseButton->setFocusPolicy(Qt::NoFocus); - - connect(m_browseButton, SIGNAL(clicked(bool)), m_browseManager, SLOT(setBrowsing(bool))); - - IQuickOpen* quickOpen = KDevelop::ICore::self()->pluginController()->extensionForPlugin<IQuickOpen>("org.kdevelop.IQuickOpen"); - - if(quickOpen) { - m_outlineLine = quickOpen->createQuickOpenLine(QStringList(), QStringList() << i18n("Outline"), IQuickOpen::Outline); - m_outlineLine->setDefaultText(i18n("Outline...")); - m_outlineLine->setToolTip(i18n("Navigate outline of active document, click to browse.")); - } - - connect(m_browseManager, SIGNAL(startDelayedBrowsing(KTextEditor::View*)), this, SIGNAL(startDelayedBrowsing(KTextEditor::View*))); - connect(m_browseManager, SIGNAL(stopDelayedBrowsing()), this, SIGNAL(stopDelayedBrowsing())); - m_allowLockedUpdate = false; - + m_buttons = new QHBoxLayout; + m_buttons->addStretch(); + m_declarationMenuButton = new QToolButton(); + m_declarationMenuButton->setIcon(KIcon("code-class")); + m_declarationMenuButton->setToolTip(i18n("Declaration menu")); + connect(m_declarationMenuButton, SIGNAL(clicked(bool)), SLOT(declarationMenu())); + m_buttons->addWidget(m_declarationMenuButton); m_lockButton = new QToolButton(); m_lockButton->setCheckable(true); m_lockButton->setChecked(false); m_lockButton->setToolTip(i18n("Lock current view")); updateLockIcon(m_lockButton->isChecked()); connect(m_lockButton, SIGNAL(toggled(bool)), SLOT(updateLockIcon(bool))); - - m_declarationMenuButton = new QToolButton(); - m_declarationMenuButton->setIcon(KIcon("code-class")); - m_declarationMenuButton->setToolTip(i18n("Declaration menu")); - connect(m_declarationMenuButton, SIGNAL(clicked(bool)), SLOT(declarationMenu())); - - m_toolbarWidget = plugin->toolbarWidgetForMainWindow(this); - m_toolbarWidgetLayout = new QHBoxLayout; - m_toolbarWidgetLayout->setSizeConstraint(QLayout::SetMaximumSize); - m_previousButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_nextButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_browseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_toolbarWidgetLayout->setMargin(0); - - m_toolbarWidgetLayout->addWidget(m_previousButton); - m_toolbarWidgetLayout->addWidget(m_outlineLine); - m_outlineLine->setMaximumWidth(600); - m_toolbarWidgetLayout->addWidget(m_nextButton); - m_toolbarWidgetLayout->addWidget(m_browseButton); - - if(m_toolbarWidget->children().isEmpty()) - m_toolbarWidget->setLayout(m_toolbarWidgetLayout); - - m_buttons->addStretch(); - m_buttons->addWidget(m_declarationMenuButton); m_buttons->addWidget(m_lockButton); m_layout = new QVBoxLayout; @@ -535,22 +138,10 @@ ContextBrowserView::ContextBrowserView( ContextBrowserPlugin* plugin, QWidget* p setLayout(m_layout); m_plugin->registerToolView(this); - - connect(ICore::self()->documentController(), SIGNAL(documentClosed(KDevelop::IDocument*)), m_outlineLine, SLOT(clear())); - connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), m_outlineLine, SLOT(clear())); } ContextBrowserView::~ContextBrowserView() { m_plugin->unRegisterToolView(this); - delete m_nextMenu; - delete m_previousMenu; - delete m_toolbarWidgetLayout; - - delete m_previousButton; - delete m_outlineLine; - delete m_nextButton; - delete m_browseButton; - } void ContextBrowserView::focusInEvent(QFocusEvent* event) { @@ -607,13 +198,16 @@ void ContextBrowserView::showEvent(QShowEvent* event) { Declaration* decl = m_navigationWidgetDeclaration.getDeclaration(top); setDeclaration(decl, top, true); //Update the declaration combo-box + /* + TODO: bring this back if required DUContext* context = 0; KDevelop::IDocument* doc = ICore::self()->documentController()->activeDocument(); if(doc && doc->textDocument() && doc->textDocument()->activeView()) { KTextEditor::Cursor c = doc->textDocument()->activeView()->cursorPosition(); context = getContextAt(top->url().toUrl(), c); } - updateDeclarationListBox(context); + m_plugin->updateDeclarationListBox(context); + */ } } QWidget::showEvent(event); @@ -699,9 +293,4 @@ void ContextBrowserView::setSpecialNavigationWidget(QWidget* widget) { } } -void ContextBrowserView::setAllowBrowsing(bool allow) { - m_browseButton->setChecked(allow); -} - - #include "contextbrowserview.moc" diff --git a/plugins/contextbrowser/contextbrowserview.h b/plugins/contextbrowser/contextbrowserview.h index bf9a6d9..8f00e61 100644 --- a/plugins/contextbrowser/contextbrowserview.h +++ b/plugins/contextbrowser/contextbrowserview.h @@ -24,12 +24,12 @@ #include <QWidget> #include <QVector> +#include <QPointer> + #include <language/duchain/topducontext.h> #include <language/editor/simplecursor.h> #include <language/editor/documentcursor.h> -#include "browsemanager.h" #include <language/duchain/indexedstring.h> -#include <language/interfaces/iquickopen.h> class ContextBrowserPlugin; class QVBoxLayout; @@ -38,7 +38,6 @@ class QToolButton; class QCheckBox; class QMenu; class KComboBox; -class BrowseManager; namespace KDevelop { class IDocument; @@ -69,65 +68,17 @@ class ContextBrowserView : public QWidget { return m_navigationWidget; } - QWidget* toolbarWidget() { - return m_toolbarWidget; - } - - ///duchain must be locked - ///@param force When this is true, the history-entry is added, no matter whether the context is "interesting" or not - void updateHistory(KDevelop::DUContext* context, const - KDevelop::SimpleCursor& cursorPosition, bool force = false); + //duchain must be locked QWidget* createWidget(KDevelop::DUContext* context); //duchain must be locked QWidget* createWidget(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext); KDevelop::IndexedDeclaration declaration() const; - - struct HistoryEntry { - //Duchain must be locked - HistoryEntry(KDevelop::IndexedDUContext ctx = KDevelop::IndexedDUContext(), const KDevelop::SimpleCursor& cursorPosition = KDevelop::SimpleCursor()); - HistoryEntry(KDevelop::DocumentCursor pos); - //Duchain must be locked - void setCursorPosition(const KDevelop::SimpleCursor& cursorPosition); - - //Duchain does not need to be locked - KDevelop::DocumentCursor computePosition() const; - - KDevelop::IndexedDUContext context; - KDevelop::DocumentCursor absoluteCursorPosition; - KDevelop::SimpleCursor relativeCursorPosition; //Cursor position relative to the start line of the context - QString alternativeString; - }; - - void updateDeclarationListBox(KDevelop::DUContext* context); - - void setAllowBrowsing(bool allow) ; - - Q_SIGNALS: - void startDelayedBrowsing(KTextEditor::View*); - void stopDelayedBrowsing(); - - public Q_SLOTS: - void navigateLeft(); - void navigateRight(); - void navigateUp(); - void navigateDown(); - void navigateAccept(); - void navigateBack(); - - void historyNext(); - void historyPrevious(); - void nextMenuAboutToShow(); - void previousMenuAboutToShow(); - void actionTriggered(); - void switchFocusToContextBrowser(); - + private Q_SLOTS: void updateLockIcon(bool); void declarationMenu(); - void documentJumpPerformed( KDevelop::IDocument* newDocument, KTextEditor::Cursor newCursor, KDevelop::IDocument* previousDocument, KTextEditor::Cursor previousCursor); - private: virtual void showEvent(QShowEvent* event); @@ -137,12 +88,6 @@ class ContextBrowserView : public QWidget { virtual void focusOutEvent(QFocusEvent* event); bool isLocked() const; void resetWidget(); - - bool isPreviousEntry(KDevelop::DUContext*, const KDevelop::SimpleCursor& cursor); - QString actionTextFor(int historyIndex); - void updateButtonState(); - void openDocument(int historyIndex); - void fillHistoryPopup(QMenu* menu, const QList<int>& historyIndices); private: @@ -152,30 +97,14 @@ class ContextBrowserView : public QWidget { QVBoxLayout* m_layout; QToolButton* m_lockButton; QToolButton* m_declarationMenuButton; - QHBoxLayout* m_buttons; + QPointer<QWidget> m_navigationWidget; KDevelop::DeclarationId m_navigationWidgetDeclaration; bool m_allowLockedUpdate; KDevelop::IndexedTopDUContext m_lastUsedTopContext; KDevelop::IndexedDUContext m_context; - int m_nextHistoryIndex; - - QVector<HistoryEntry> m_history; - QPointer<QToolButton> m_previousButton; - QPointer<QToolButton> m_nextButton; - QPointer<QMenu> m_previousMenu, m_nextMenu; - QPointer<QToolButton> m_browseButton; - QList<KDevelop::IndexedDeclaration> m_listDeclarations; - KDevelop::IndexedString m_listUrl; - BrowseManager* m_browseManager; - //Used to not record jumps triggered by the context-browser as history entries - QPointer<QWidget> m_focusBackWidget; - QPointer<KDevelop::IQuickOpenLine> m_outlineLine; - - QPointer<QHBoxLayout> m_toolbarWidgetLayout; - QPointer<QWidget> m_toolbarWidget; }; #endif |