mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
1550 lines
48 KiB
C++
1550 lines
48 KiB
C++
/*
|
|
* This file is part of KDevelop
|
|
*
|
|
* Copyright 2007 David Nolden <david.nolden.kdevelop@art-master.de>
|
|
*
|
|
* 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 "quickopenplugin.h"
|
|
|
|
#include <cassert>
|
|
#include <typeinfo>
|
|
#include <QtGui/QTreeView>
|
|
#include <QtGui/QHeaderView>
|
|
#include <QDialog>
|
|
#include <QKeyEvent>
|
|
#include <QApplication>
|
|
#include <QScrollBar>
|
|
#include <QCheckBox>
|
|
#include <QMetaObject>
|
|
|
|
#include <kbuttongroup.h>
|
|
#include <klocale.h>
|
|
#include <kpluginfactory.h>
|
|
#include <kpluginloader.h>
|
|
#include <kaboutdata.h>
|
|
#include <ktexteditor/document.h>
|
|
#include <ktexteditor/view.h>
|
|
#include <kparts/mainwindow.h>
|
|
#include <kactioncollection.h>
|
|
#include <kaction.h>
|
|
#include <kshortcut.h>
|
|
#include <kdebug.h>
|
|
|
|
#include <interfaces/ilanguage.h>
|
|
#include <interfaces/icore.h>
|
|
#include <interfaces/iuicontroller.h>
|
|
#include <interfaces/idocumentcontroller.h>
|
|
#include <interfaces/ilanguagecontroller.h>
|
|
#include <interfaces/iprojectcontroller.h>
|
|
#include <language/interfaces/ilanguagesupport.h>
|
|
#include <language/duchain/duchainutils.h>
|
|
#include <language/duchain/duchainlock.h>
|
|
#include <language/duchain/duchain.h>
|
|
#include <language/duchain/types/identifiedtype.h>
|
|
#include <language/duchain/indexedstring.h>
|
|
#include <language/duchain/types/functiontype.h>
|
|
|
|
#include "expandingtree/expandingdelegate.h"
|
|
#include "ui_quickopen.h"
|
|
#include "quickopenmodel.h"
|
|
#include "projectfilequickopen.h"
|
|
#include "projectitemquickopen.h"
|
|
#include "declarationlistquickopen.h"
|
|
#include "documentationquickopenprovider.h"
|
|
#include <language/duchain/functiondefinition.h>
|
|
#include <qmenu.h>
|
|
#include <qdesktopwidget.h>
|
|
#include <util/activetooltip.h>
|
|
#include <qboxlayout.h>
|
|
#include <language/util/navigationtooltip.h>
|
|
#include <interfaces/contextmenuextension.h>
|
|
#include <language/interfaces/codecontext.h>
|
|
|
|
using namespace KDevelop;
|
|
|
|
const bool noHtmlDestriptionInOutline = true;
|
|
|
|
class QuickOpenWidgetCreator {
|
|
public:
|
|
virtual ~QuickOpenWidgetCreator() {
|
|
}
|
|
virtual QuickOpenWidget* createWidget() = 0;
|
|
virtual QString objectNameForLine() = 0;
|
|
virtual void widgetShown() {
|
|
}
|
|
};
|
|
|
|
class StandardQuickOpenWidgetCreator : public QuickOpenWidgetCreator {
|
|
public:
|
|
StandardQuickOpenWidgetCreator(const QStringList& items, const QStringList& scopes)
|
|
: m_items(items)
|
|
, m_scopes(scopes)
|
|
{
|
|
}
|
|
|
|
virtual QString objectNameForLine() {
|
|
return "Quickopen";
|
|
}
|
|
|
|
void setItems(const QStringList& scopes, const QStringList& items)
|
|
{
|
|
m_scopes = scopes;
|
|
m_items = items;
|
|
}
|
|
|
|
virtual QuickOpenWidget* createWidget() {
|
|
QStringList useItems = m_items;
|
|
if(useItems.isEmpty())
|
|
useItems = QuickOpenPlugin::self()->lastUsedItems;
|
|
|
|
QStringList useScopes = m_scopes;
|
|
if(useScopes.isEmpty())
|
|
useScopes = QuickOpenPlugin::self()->lastUsedScopes;
|
|
|
|
return new QuickOpenWidget( i18n("Quick Open"), QuickOpenPlugin::self()->m_model, QuickOpenPlugin::self()->lastUsedItems, useScopes, false, true );
|
|
}
|
|
|
|
QStringList m_items;
|
|
QStringList m_scopes;
|
|
};
|
|
|
|
class QuickOpenDelegate : public ExpandingDelegate {
|
|
public:
|
|
QuickOpenDelegate(ExpandingWidgetModel* model, QObject* parent = 0L) : ExpandingDelegate(model, parent) {
|
|
}
|
|
virtual QList<QTextLayout::FormatRange> createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const {
|
|
QList<QVariant> highlighting = index.data(KTextEditor::CodeCompletionModel::CustomHighlight).toList();
|
|
if(!highlighting.isEmpty())
|
|
return highlightingFromVariantList(highlighting);
|
|
return ExpandingDelegate::createHighlighting( index, option );
|
|
}
|
|
|
|
};
|
|
|
|
class OutlineFilter : public DUChainUtils::DUChainItemFilter {
|
|
public:
|
|
enum OutlineMode { Functions, FunctionsAndClasses };
|
|
OutlineFilter(QList<DUChainItem>& _items, OutlineMode _mode = FunctionsAndClasses) : items(_items), mode(_mode) {
|
|
}
|
|
virtual bool accept(Declaration* decl) {
|
|
if(decl->range().isEmpty())
|
|
return false;
|
|
bool collectable = mode == Functions ? decl->isFunctionDeclaration() : (decl->isFunctionDeclaration() || (decl->internalContext() && decl->internalContext()->type() == DUContext::Class));
|
|
if (collectable) {
|
|
DUChainItem item;
|
|
item.m_item = IndexedDeclaration(decl);
|
|
item.m_text = decl->toString();
|
|
items << item;
|
|
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
virtual bool accept(DUContext* ctx) {
|
|
if(ctx->type() == DUContext::Class || ctx->type() == DUContext::Namespace || ctx->type() == DUContext::Global || ctx->type() == DUContext::Other || ctx->type() == DUContext::Helper )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
QList<DUChainItem>& items;
|
|
OutlineMode mode;
|
|
};
|
|
|
|
K_PLUGIN_FACTORY(KDevQuickOpenFactory, registerPlugin<QuickOpenPlugin>(); )
|
|
K_EXPORT_PLUGIN(KDevQuickOpenFactory(KAboutData("kdevquickopen","kdevquickopen", ki18n("Quick Open"), "0.1", ki18n("This plugin allows quick access to project files and language-items like classes/functions."), KAboutData::License_GPL)))
|
|
|
|
Declaration* cursorDeclaration() {
|
|
IDocument* doc = ICore::self()->documentController()->activeDocument();
|
|
if(!doc)
|
|
return 0;
|
|
|
|
KTextEditor::Document* textDoc = doc->textDocument();
|
|
if(!textDoc)
|
|
return 0;
|
|
|
|
KTextEditor::View* view = textDoc->activeView();
|
|
if(!view)
|
|
return 0;
|
|
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
|
|
return DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor( doc->url(), SimpleCursor(view->cursorPosition()) ) );
|
|
}
|
|
|
|
///The first definition that belongs to a context that surrounds the current cursor
|
|
Declaration* cursorContextDeclaration() {
|
|
IDocument* doc = ICore::self()->documentController()->activeDocument();
|
|
if(!doc)
|
|
return 0;
|
|
|
|
KTextEditor::Document* textDoc = doc->textDocument();
|
|
if(!textDoc)
|
|
return 0;
|
|
|
|
KTextEditor::View* view = textDoc->activeView();
|
|
if(!view)
|
|
return 0;
|
|
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
|
|
TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url());
|
|
if(!ctx)
|
|
return 0;
|
|
|
|
SimpleCursor cursor(view->cursorPosition());
|
|
|
|
DUContext* subCtx = ctx->findContext(ctx->transformToLocalRevision(cursor));
|
|
|
|
while(subCtx && !subCtx->owner())
|
|
subCtx = subCtx->parentContext();
|
|
|
|
Declaration* definition = 0;
|
|
|
|
if(!subCtx || !subCtx->owner())
|
|
definition = DUChainUtils::declarationInLine(cursor, ctx);
|
|
else
|
|
definition = subCtx->owner();
|
|
|
|
if(!definition)
|
|
return 0;
|
|
|
|
return definition;
|
|
}
|
|
|
|
//Returns only the name, no template-parameters or scope
|
|
QString cursorItemText() {
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
|
|
Declaration* decl = cursorDeclaration();
|
|
if(!decl)
|
|
return QString();
|
|
|
|
IDocument* doc = ICore::self()->documentController()->activeDocument();
|
|
if(!doc)
|
|
return QString();
|
|
|
|
TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() );
|
|
|
|
if( !context ) {
|
|
kDebug() << "Got no standard context";
|
|
return QString();
|
|
}
|
|
|
|
AbstractType::Ptr t = decl->abstractType();
|
|
IdentifiedType* idType = dynamic_cast<IdentifiedType*>(t.unsafeData());
|
|
if( idType && idType->declaration(context) )
|
|
decl = idType->declaration(context);
|
|
|
|
if(!decl->qualifiedIdentifier().isEmpty())
|
|
return decl->qualifiedIdentifier().last().identifier().str();
|
|
|
|
return QString();
|
|
}
|
|
|
|
QuickOpenLineEdit* QuickOpenPlugin::createQuickOpenLineWidget()
|
|
{
|
|
return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(QStringList(), QStringList()));
|
|
}
|
|
|
|
void QuickOpenWidget::showStandardButtons(bool show)
|
|
{
|
|
if(show) {
|
|
o.okButton->show();
|
|
o.cancelButton->show();
|
|
}else{
|
|
o.okButton->hide();
|
|
o.cancelButton->hide();
|
|
}
|
|
}
|
|
|
|
QuickOpenWidget::QuickOpenWidget( QString title, QuickOpenModel* model, const QStringList& initialItems, const QStringList& initialScopes, bool listOnly, bool noSearchField ) : m_model(model), m_expandedTemporary(false) {
|
|
m_filterTimer.setSingleShot(true);
|
|
connect(&m_filterTimer, SIGNAL(timeout()), this, SLOT(applyFilter()));
|
|
|
|
Q_UNUSED( title );
|
|
o.setupUi( this );
|
|
o.list->header()->hide();
|
|
o.list->setRootIsDecorated( false );
|
|
o.list->setVerticalScrollMode( QAbstractItemView::ScrollPerItem );
|
|
|
|
connect(o.list->verticalScrollBar(), SIGNAL(valueChanged(int)), m_model, SLOT(placeExpandingWidgets()));
|
|
|
|
o.searchLine->setFocus();
|
|
|
|
o.list->setItemDelegate( new QuickOpenDelegate( m_model, o.list ) );
|
|
|
|
if(!listOnly) {
|
|
QStringList allTypes = m_model->allTypes();
|
|
QStringList allScopes = m_model->allScopes();
|
|
|
|
QMenu* itemsMenu = new QMenu;
|
|
|
|
foreach( const QString &type, allTypes )
|
|
{
|
|
QAction* action = new QAction(type, itemsMenu);
|
|
action->setCheckable(true);
|
|
action->setChecked(initialItems.isEmpty() || initialItems.contains( type ));
|
|
connect( action, SIGNAL(toggled(bool)), this, SLOT(updateProviders()), Qt::QueuedConnection );
|
|
itemsMenu->addAction(action);
|
|
}
|
|
|
|
o.itemsButton->setMenu(itemsMenu);
|
|
|
|
QMenu* scopesMenu = new QMenu;
|
|
|
|
foreach( const QString &scope, allScopes )
|
|
{
|
|
QAction* action = new QAction(scope, scopesMenu);
|
|
action->setCheckable(true);
|
|
action->setChecked(initialScopes.isEmpty() || initialScopes.contains( scope ) );
|
|
|
|
connect( action, SIGNAL(toggled(bool)), this, SLOT(updateProviders()), Qt::QueuedConnection );
|
|
scopesMenu->addAction(action);
|
|
}
|
|
|
|
o.scopesButton->setMenu(scopesMenu);
|
|
|
|
}else{
|
|
o.list->setFocusPolicy(Qt::StrongFocus);
|
|
o.scopesButton->hide();
|
|
o.itemsButton->hide();
|
|
o.label->hide();
|
|
o.label_2->hide();
|
|
}
|
|
|
|
showSearchField(!noSearchField);
|
|
|
|
o.okButton->hide();
|
|
o.cancelButton->hide();
|
|
|
|
o.searchLine->installEventFilter( this );
|
|
o.list->installEventFilter( this );
|
|
o.list->setFocusPolicy(Qt::NoFocus);
|
|
o.scopesButton->setFocusPolicy(Qt::NoFocus);
|
|
o.itemsButton->setFocusPolicy(Qt::NoFocus);
|
|
|
|
connect( o.searchLine, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) );
|
|
|
|
connect( o.list, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(doubleClicked(QModelIndex)) );
|
|
|
|
connect(o.okButton, SIGNAL(clicked(bool)), this, SLOT(accept()));
|
|
connect(o.okButton, SIGNAL(clicked(bool)), SIGNAL(ready()));
|
|
connect(o.cancelButton, SIGNAL(clicked(bool)), SIGNAL(ready()));
|
|
|
|
updateProviders();
|
|
updateTimerInterval(true);
|
|
|
|
// no need to call this, it's done by updateProviders already
|
|
// m_model->restart();
|
|
}
|
|
|
|
void QuickOpenWidget::updateTimerInterval(bool cheapFilterChange)
|
|
{
|
|
const int MAX_ITEMS = 10000;
|
|
if ( cheapFilterChange && m_model->rowCount(QModelIndex()) < MAX_ITEMS ) {
|
|
// cheap change and there are currently just a few items,
|
|
// so apply filter instantly
|
|
m_filterTimer.setInterval(0);
|
|
} else if ( m_model->unfilteredRowCount() < MAX_ITEMS ) {
|
|
// not a cheap change, but there are generally
|
|
// just a few items in the list: apply filter instantly
|
|
m_filterTimer.setInterval(0);
|
|
} else {
|
|
// otherwise use a timer to prevent sluggishness while typing
|
|
m_filterTimer.setInterval(300);
|
|
}
|
|
}
|
|
|
|
void QuickOpenWidget::showEvent(QShowEvent* e)
|
|
{
|
|
QWidget::showEvent(e);
|
|
|
|
// The column width only has an effect _after_ the widget has been shown
|
|
o.list->setColumnWidth( 0, 20 );
|
|
}
|
|
|
|
void QuickOpenWidget::setAlternativeSearchField(KLineEdit* alterantiveSearchField)
|
|
{
|
|
o.searchLine = alterantiveSearchField;
|
|
o.searchLine->installEventFilter( this );
|
|
connect( o.searchLine, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) );
|
|
}
|
|
|
|
|
|
void QuickOpenWidget::showSearchField(bool b)
|
|
{
|
|
if(b){
|
|
o.searchLine->show();
|
|
o.searchLabel->show();
|
|
}else{
|
|
o.searchLine->hide();
|
|
o.searchLabel->hide();
|
|
}
|
|
}
|
|
|
|
void QuickOpenWidget::prepareShow()
|
|
{
|
|
o.list->setModel( 0 );
|
|
o.list->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
|
m_model->setTreeView( o.list );
|
|
o.list->setModel( m_model );
|
|
|
|
m_filterTimer.stop();
|
|
m_filter = QString();
|
|
|
|
if (!m_preselectedText.isEmpty())
|
|
{
|
|
o.searchLine->setText(m_preselectedText);
|
|
o.searchLine->selectAll();
|
|
}
|
|
|
|
m_model->restart(false);
|
|
|
|
connect( o.list->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
|
SLOT(callRowSelected()) );
|
|
connect( o.list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
SLOT(callRowSelected()) );
|
|
}
|
|
|
|
void QuickOpenWidgetDialog::run() {
|
|
m_widget->prepareShow();
|
|
m_dialog->show();
|
|
}
|
|
|
|
QuickOpenWidget::~QuickOpenWidget() {
|
|
m_model->setTreeView( 0 );
|
|
}
|
|
|
|
|
|
QuickOpenWidgetDialog::QuickOpenWidgetDialog(QString title, QuickOpenModel* model, const QStringList& initialItems, const QStringList& initialScopes, bool listOnly, bool noSearchField)
|
|
{
|
|
m_widget = new QuickOpenWidget(title, model, initialItems, initialScopes, listOnly, noSearchField);
|
|
// the QMenu might close on esc and we want to close the whole dialog then
|
|
connect( m_widget, SIGNAL(aboutToHide()), this, SLOT(deleteLater()) );
|
|
|
|
//KDialog always sets the focus on the "OK" button, so we use QDialog
|
|
m_dialog = new QDialog( ICore::self()->uiController()->activeMainWindow() );
|
|
m_dialog->resize(QSize(800, 400));
|
|
|
|
m_dialog->setWindowTitle(title);
|
|
QVBoxLayout* layout = new QVBoxLayout(m_dialog);
|
|
layout->addWidget(m_widget);
|
|
m_widget->showStandardButtons(true);
|
|
connect(m_widget, SIGNAL(ready()), m_dialog, SLOT(close()));
|
|
connect( m_dialog, SIGNAL(accepted()), m_widget, SLOT(accept()) );
|
|
}
|
|
|
|
|
|
QuickOpenWidgetDialog::~QuickOpenWidgetDialog()
|
|
{
|
|
delete m_dialog;
|
|
}
|
|
|
|
void QuickOpenWidget::setPreselectedText(const QString& text)
|
|
{
|
|
m_preselectedText = text;
|
|
}
|
|
|
|
void QuickOpenWidget::updateProviders() {
|
|
if(QAction* action = qobject_cast<QAction*>(sender())) {
|
|
QMenu* menu = qobject_cast<QMenu*>(action->parentWidget());
|
|
if(menu) {
|
|
menu->show();
|
|
menu->setActiveAction(action);
|
|
}
|
|
}
|
|
|
|
QStringList checkedItems;
|
|
|
|
if(o.itemsButton->menu()) {
|
|
|
|
foreach( QObject* obj, o.itemsButton->menu()->children() ) {
|
|
QAction* box = qobject_cast<QAction*>( obj );
|
|
if( box ) {
|
|
if( box->isChecked() )
|
|
checkedItems << box->text().remove('&');
|
|
}
|
|
}
|
|
o.itemsButton->setText(checkedItems.join(", "));
|
|
}
|
|
|
|
QStringList checkedScopes;
|
|
|
|
if(o.scopesButton->menu()) {
|
|
|
|
foreach( QObject* obj, o.scopesButton->menu()->children() ) {
|
|
QAction* box = qobject_cast<QAction*>( obj );
|
|
if( box ) {
|
|
if( box->isChecked() )
|
|
checkedScopes << box->text().remove('&');
|
|
}
|
|
}
|
|
|
|
o.scopesButton->setText(checkedScopes.join(", "));
|
|
}
|
|
|
|
emit itemsChanged( checkedItems );
|
|
emit scopesChanged( checkedScopes );
|
|
m_model->enableProviders( checkedItems, checkedScopes );
|
|
}
|
|
|
|
void QuickOpenWidget::textChanged( const QString& str )
|
|
{
|
|
// "cheap" when something was just appended to the current filter
|
|
updateTimerInterval(str.startsWith(m_filter));
|
|
m_filter = str;
|
|
m_filterTimer.start();
|
|
}
|
|
|
|
void QuickOpenWidget::applyFilter()
|
|
{
|
|
m_model->textChanged( m_filter );
|
|
|
|
QModelIndex currentIndex = m_model->index(0, 0, QModelIndex());
|
|
o.list->selectionModel()->setCurrentIndex( currentIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current );
|
|
|
|
callRowSelected();
|
|
}
|
|
|
|
void QuickOpenWidget::callRowSelected() {
|
|
QModelIndex currentIndex = o.list->selectionModel()->currentIndex();
|
|
if( currentIndex.isValid() )
|
|
m_model->rowSelected( currentIndex );
|
|
else
|
|
kDebug() << "current index is not valid";
|
|
}
|
|
|
|
void QuickOpenWidget::accept() {
|
|
QString filterText = o.searchLine->text();
|
|
m_model->execute( o.list->currentIndex(), filterText );
|
|
}
|
|
|
|
void QuickOpenWidget::doubleClicked ( const QModelIndex & index ) {
|
|
// crash guard: https://bugs.kde.org/show_bug.cgi?id=297178
|
|
o.list->setCurrentIndex(index);
|
|
QMetaObject::invokeMethod(this, "accept", Qt::QueuedConnection);
|
|
QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);
|
|
}
|
|
|
|
bool QuickOpenWidget::eventFilter ( QObject * watched, QEvent * event )
|
|
{
|
|
QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
|
|
|
|
if( event->type() == QEvent::KeyRelease ) {
|
|
if(keyEvent->key() == Qt::Key_Alt) {
|
|
if((m_expandedTemporary && m_altDownTime.msecsTo( QTime::currentTime() ) > 300) || (!m_expandedTemporary && m_altDownTime.msecsTo( QTime::currentTime() ) < 300 && m_hadNoCommandSinceAlt)) {
|
|
//Unexpand the item
|
|
QModelIndex row = o.list->selectionModel()->currentIndex();
|
|
if( row.isValid() ) {
|
|
row = row.sibling( row.row(), 0 );
|
|
if(m_model->isExpanded( row ))
|
|
m_model->setExpanded( row, false );
|
|
}
|
|
}
|
|
m_expandedTemporary = false;
|
|
}
|
|
}
|
|
|
|
if( event->type() == QEvent::KeyPress ) {
|
|
m_hadNoCommandSinceAlt = false;
|
|
if(keyEvent->key() == Qt::Key_Alt) {
|
|
m_hadNoCommandSinceAlt = true;
|
|
//Expand
|
|
QModelIndex row = o.list->selectionModel()->currentIndex();
|
|
if( row.isValid() ) {
|
|
row = row.sibling( row.row(), 0 );
|
|
m_altDownTime = QTime::currentTime();
|
|
if(!m_model->isExpanded( row )) {
|
|
m_expandedTemporary = true;
|
|
m_model->setExpanded( row, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
switch( keyEvent->key() ) {
|
|
case Qt::Key_Tab:
|
|
if ( keyEvent->modifiers() == Qt::NoModifier ) {
|
|
// Tab should work just like Down
|
|
QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier));
|
|
QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Down, Qt::NoModifier));
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Backtab:
|
|
if ( keyEvent->modifiers() == Qt::ShiftModifier ) {
|
|
// Shift + Tab should work just like Up
|
|
QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier));
|
|
QCoreApplication::sendEvent(o.list, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Up, Qt::NoModifier));
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Down:
|
|
case Qt::Key_Up:
|
|
{
|
|
if( keyEvent->modifiers() == Qt::AltModifier ) {
|
|
QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
|
|
if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
|
|
dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
|
|
if( keyEvent->key() == Qt::Key_Down )
|
|
interface->down();
|
|
else
|
|
interface->up();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
case Qt::Key_PageUp:
|
|
case Qt::Key_PageDown:
|
|
if(watched == o.list )
|
|
return false;
|
|
QApplication::sendEvent( o.list, event );
|
|
//callRowSelected();
|
|
return true;
|
|
|
|
case Qt::Key_Left: {
|
|
//Expand/unexpand
|
|
if( keyEvent->modifiers() == Qt::AltModifier ) {
|
|
//Eventually Send action to the widget
|
|
QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
|
|
if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
|
|
dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
|
|
interface->previous();
|
|
return true;
|
|
}
|
|
} else {
|
|
QModelIndex row = o.list->selectionModel()->currentIndex();
|
|
if( row.isValid() ) {
|
|
row = row.sibling( row.row(), 0 );
|
|
|
|
if( m_model->isExpanded( row ) ) {
|
|
m_model->setExpanded( row, false );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
case Qt::Key_Right: {
|
|
//Expand/unexpand
|
|
if( keyEvent->modifiers() == Qt::AltModifier ) {
|
|
//Eventually Send action to the widget
|
|
QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
|
|
if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
|
|
dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
|
|
interface->next();
|
|
return true;
|
|
}
|
|
} else {
|
|
QModelIndex row = o.list->selectionModel()->currentIndex();
|
|
if( row.isValid() ) {
|
|
row = row.sibling( row.row(), 0 );
|
|
|
|
if( !m_model->isExpanded( row ) ) {
|
|
m_model->setExpanded( row, true );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Enter: {
|
|
if (m_filterTimer.isActive()) {
|
|
m_filterTimer.stop();
|
|
applyFilter();
|
|
}
|
|
if( keyEvent->modifiers() == Qt::AltModifier ) {
|
|
//Eventually Send action to the widget
|
|
QWidget* w = m_model->expandingWidget(o.list->selectionModel()->currentIndex());
|
|
if( KDevelop::QuickOpenEmbeddedWidgetInterface* interface =
|
|
dynamic_cast<KDevelop::QuickOpenEmbeddedWidgetInterface*>( w ) ){
|
|
interface->accept();
|
|
return true;
|
|
}
|
|
} else {
|
|
QString filterText = o.searchLine->text();
|
|
|
|
//Safety: Track whether this object is deleted. When execute() is called, a dialog may be opened,
|
|
//which kills the quickopen widget.
|
|
QPointer<QObject> stillExists(this);
|
|
|
|
if( m_model->execute( o.list->currentIndex(), filterText ) ) {
|
|
|
|
if(!stillExists)
|
|
return true;
|
|
|
|
if(!(keyEvent->modifiers() & Qt::ShiftModifier))
|
|
emit ready();
|
|
} else {
|
|
//Maybe the filter-text was changed:
|
|
if( filterText != o.searchLine->text() ) {
|
|
o.searchLine->setText( filterText );
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
QuickOpenLineEdit* QuickOpenPlugin::quickOpenLine(QString name)
|
|
{
|
|
QList< QuickOpenLineEdit* > lines = ICore::self()->uiController()->activeMainWindow()->findChildren<QuickOpenLineEdit*>(name);
|
|
foreach(QuickOpenLineEdit* line, lines) {
|
|
if(line->isVisible()) {
|
|
return line;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static QuickOpenPlugin* staticQuickOpenPlugin = 0;
|
|
|
|
QuickOpenPlugin* QuickOpenPlugin::self()
|
|
{
|
|
return staticQuickOpenPlugin;
|
|
}
|
|
|
|
void QuickOpenPlugin::createActionsForMainWindow(Sublime::MainWindow* /*window*/, QString& xmlFile, KActionCollection& actions)
|
|
{
|
|
xmlFile ="kdevquickopen.rc";
|
|
|
|
KAction* quickOpen = actions.addAction("quick_open");
|
|
quickOpen->setText( i18n("&Quick Open") );
|
|
quickOpen->setIcon( KIcon("quickopen") );
|
|
quickOpen->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_Q );
|
|
connect(quickOpen, SIGNAL(triggered(bool)), this, SLOT(quickOpen()));
|
|
|
|
KAction* quickOpenFile = actions.addAction("quick_open_file");
|
|
quickOpenFile->setText( i18n("Quick Open &File") );
|
|
quickOpenFile->setIcon( KIcon("quickopen-file") );
|
|
quickOpenFile->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_O );
|
|
connect(quickOpenFile, SIGNAL(triggered(bool)), this, SLOT(quickOpenFile()));
|
|
|
|
KAction* quickOpenClass = actions.addAction("quick_open_class");
|
|
quickOpenClass->setText( i18n("Quick Open &Class") );
|
|
quickOpenClass->setIcon( KIcon("quickopen-class") );
|
|
quickOpenClass->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_C );
|
|
connect(quickOpenClass, SIGNAL(triggered(bool)), this, SLOT(quickOpenClass()));
|
|
|
|
KAction* quickOpenFunction = actions.addAction("quick_open_function");
|
|
quickOpenFunction->setText( i18n("Quick Open &Function") );
|
|
quickOpenFunction->setIcon( KIcon("quickopen-function") );
|
|
quickOpenFunction->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_M );
|
|
connect(quickOpenFunction, SIGNAL(triggered(bool)), this, SLOT(quickOpenFunction()));
|
|
|
|
KAction* quickOpenAlreadyOpen = actions.addAction("quick_open_already_open");
|
|
quickOpenAlreadyOpen->setText( i18n("Quick Open &Already Open File") );
|
|
quickOpenAlreadyOpen->setIcon( KIcon("quickopen-file") );
|
|
connect(quickOpenAlreadyOpen, SIGNAL(triggered(bool)), this, SLOT(quickOpenOpenFile()));
|
|
|
|
KAction* quickOpenDocumentation = actions.addAction("quick_open_documentation");
|
|
quickOpenDocumentation->setText( i18n("Quick Open &Documentation") );
|
|
quickOpenDocumentation->setIcon( KIcon("quickopen-documentation") );
|
|
quickOpenDocumentation->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_D );
|
|
connect(quickOpenDocumentation, SIGNAL(triggered(bool)), this, SLOT(quickOpenDocumentation()));
|
|
|
|
m_quickOpenDeclaration = actions.addAction("quick_open_jump_declaration");
|
|
m_quickOpenDeclaration->setText( i18n("Jump to Declaration") );
|
|
m_quickOpenDeclaration->setIcon( KIcon("go-jump-declaration" ) );
|
|
m_quickOpenDeclaration->setShortcut( Qt::CTRL | Qt::Key_Period );
|
|
connect(m_quickOpenDeclaration, SIGNAL(triggered(bool)), this, SLOT(quickOpenDeclaration()), Qt::QueuedConnection);
|
|
|
|
m_quickOpenDefinition = actions.addAction("quick_open_jump_definition");
|
|
m_quickOpenDefinition->setText( i18n("Jump to Definition") );
|
|
m_quickOpenDefinition->setIcon( KIcon("go-jump-definition" ) );
|
|
m_quickOpenDefinition->setShortcut( Qt::CTRL | Qt::Key_Comma );
|
|
connect(m_quickOpenDefinition, SIGNAL(triggered(bool)), this, SLOT(quickOpenDefinition()), Qt::QueuedConnection);
|
|
|
|
KAction* quickOpenLine = actions.addAction("quick_open_line");
|
|
quickOpenLine->setText( i18n("Embedded Quick Open") );
|
|
// quickOpenLine->setShortcut( Qt::CTRL | Qt::ALT | Qt::Key_E );
|
|
// connect(quickOpenLine, SIGNAL(triggered(bool)), this, SLOT(quickOpenLine(bool)));
|
|
quickOpenLine->setDefaultWidget(createQuickOpenLineWidget());
|
|
|
|
KAction* quickOpenNextFunction = actions.addAction("quick_open_next_function");
|
|
quickOpenNextFunction->setText( i18n("Next Function") );
|
|
quickOpenNextFunction->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_PageDown );
|
|
connect(quickOpenNextFunction, SIGNAL(triggered(bool)), this, SLOT(nextFunction()));
|
|
|
|
KAction* quickOpenPrevFunction = actions.addAction("quick_open_prev_function");
|
|
quickOpenPrevFunction->setText( i18n("Previous Function") );
|
|
quickOpenPrevFunction->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_PageUp );
|
|
connect(quickOpenPrevFunction, SIGNAL(triggered(bool)), this, SLOT(previousFunction()));
|
|
|
|
KAction* quickOpenNavigateFunctions = actions.addAction("quick_open_outline");
|
|
quickOpenNavigateFunctions->setText( i18n("Outline") );
|
|
quickOpenNavigateFunctions->setShortcut( Qt::CTRL| Qt::ALT | Qt::Key_N );
|
|
connect(quickOpenNavigateFunctions, SIGNAL(triggered(bool)), this, SLOT(quickOpenNavigateFunctions()));
|
|
}
|
|
|
|
QuickOpenPlugin::QuickOpenPlugin(QObject *parent,
|
|
const QVariantList&)
|
|
: KDevelop::IPlugin(KDevQuickOpenFactory::componentData(), parent)
|
|
{
|
|
staticQuickOpenPlugin = this;
|
|
KDEV_USE_EXTENSION_INTERFACE( KDevelop::IQuickOpen )
|
|
m_model = new QuickOpenModel( 0 );
|
|
|
|
KConfigGroup quickopengrp = KGlobal::config()->group("QuickOpen");
|
|
lastUsedScopes = quickopengrp.readEntry("SelectedScopes", QStringList() << i18n("Project") << i18n("Includes") << i18n("Includers") << i18n("Currently Open") );
|
|
lastUsedItems = quickopengrp.readEntry("SelectedItems", QStringList() );
|
|
|
|
{
|
|
m_openFilesData = new OpenFilesDataProvider();
|
|
QStringList scopes, items;
|
|
scopes << i18n("Currently Open");
|
|
items << i18n("Files");
|
|
m_model->registerProvider( scopes, items, m_openFilesData );
|
|
}
|
|
{
|
|
m_projectFileData = new ProjectFileDataProvider();
|
|
QStringList scopes, items;
|
|
scopes << i18n("Project");
|
|
items << i18n("Files");
|
|
m_model->registerProvider( scopes, items, m_projectFileData );
|
|
}
|
|
{
|
|
m_projectItemData = new ProjectItemDataProvider(this);
|
|
QStringList scopes, items;
|
|
scopes << i18n("Project");
|
|
items << ProjectItemDataProvider::supportedItemTypes();
|
|
m_model->registerProvider( scopes, items, m_projectItemData );
|
|
}
|
|
{
|
|
m_documentationItemData = new DocumentationQuickOpenProvider;
|
|
QStringList scopes, items;
|
|
scopes << i18n("Includes");
|
|
items << i18n("Documentation");
|
|
m_model->registerProvider( scopes, items, m_documentationItemData );
|
|
}
|
|
}
|
|
|
|
QuickOpenPlugin::~QuickOpenPlugin()
|
|
{
|
|
freeModel();
|
|
|
|
delete m_model;
|
|
delete m_projectFileData;
|
|
delete m_projectItemData;
|
|
delete m_openFilesData;
|
|
delete m_documentationItemData;
|
|
}
|
|
|
|
void QuickOpenPlugin::unload()
|
|
{
|
|
}
|
|
|
|
ContextMenuExtension QuickOpenPlugin::contextMenuExtension(Context* context)
|
|
{
|
|
KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context );
|
|
|
|
KDevelop::DeclarationContext *codeContext = dynamic_cast<KDevelop::DeclarationContext*>(context);
|
|
|
|
if (!codeContext)
|
|
return menuExt;
|
|
|
|
DUChainReadLocker readLock;
|
|
Declaration* decl(codeContext->declaration().data());
|
|
|
|
if (decl) {
|
|
const bool isDef = FunctionDefinition::definition(decl);
|
|
if (codeContext->use().isValid() || !isDef) {
|
|
menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_quickOpenDeclaration);
|
|
}
|
|
|
|
if(isDef) {
|
|
menuExt.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, m_quickOpenDefinition);
|
|
}
|
|
}
|
|
|
|
return menuExt;
|
|
}
|
|
|
|
void QuickOpenPlugin::showQuickOpen(const QStringList& items)
|
|
{
|
|
if(!freeModel())
|
|
return;
|
|
|
|
QStringList initialItems = items;
|
|
|
|
QStringList useScopes = lastUsedScopes;
|
|
|
|
if (!useScopes.contains(i18n("Currently Open")))
|
|
useScopes << i18n("Currently Open");
|
|
|
|
showQuickOpenWidget(initialItems, useScopes, false);
|
|
}
|
|
|
|
void QuickOpenPlugin::showQuickOpen( ModelTypes modes )
|
|
{
|
|
if(!freeModel())
|
|
return;
|
|
|
|
QStringList initialItems;
|
|
if( modes & Files || modes & OpenFiles )
|
|
initialItems << i18n("Files");
|
|
|
|
if( modes & Functions )
|
|
initialItems << i18n("Functions");
|
|
|
|
if( modes & Classes )
|
|
initialItems << i18n("Classes");
|
|
|
|
QStringList useScopes;
|
|
if ( modes != OpenFiles )
|
|
useScopes = lastUsedScopes;
|
|
|
|
if((modes & OpenFiles) && !useScopes.contains(i18n("Currently Open")))
|
|
useScopes << i18n("Currently Open");
|
|
|
|
bool preselectText = (!(modes & Files) || modes == QuickOpenPlugin::All);
|
|
showQuickOpenWidget(initialItems, useScopes, preselectText);
|
|
}
|
|
|
|
void QuickOpenPlugin::showQuickOpenWidget(const QStringList& items, const QStringList& scopes, bool preselectText)
|
|
{
|
|
QuickOpenWidgetDialog* dialog = new QuickOpenWidgetDialog( i18n("Quick Open"), m_model, items, scopes );
|
|
m_currentWidgetHandler = dialog;
|
|
if (preselectText)
|
|
{
|
|
KDevelop::IDocument *currentDoc = core()->documentController()->activeDocument();
|
|
if (currentDoc && currentDoc->isTextDocument())
|
|
{
|
|
QString preselected = currentDoc->textSelection().isEmpty() ? currentDoc->textWord() : currentDoc->textDocument()->text(currentDoc->textSelection());
|
|
dialog->widget()->setPreselectedText(preselected);
|
|
}
|
|
}
|
|
|
|
connect( dialog->widget(), SIGNAL(scopesChanged(QStringList)), this, SLOT(storeScopes(QStringList)) );
|
|
//Not connecting itemsChanged to storeItems, as showQuickOpen doesn't use lastUsedItems and so shouldn't store item changes
|
|
//connect( dialog->widget(), SIGNAL(itemsChanged(QStringList)), this, SLOT(storeItems(QStringList)) );
|
|
dialog->widget()->o.itemsButton->setEnabled(false);
|
|
|
|
if(quickOpenLine()) {
|
|
quickOpenLine()->showWithWidget(dialog->widget());
|
|
dialog->deleteLater();
|
|
}else{
|
|
dialog->run();
|
|
}
|
|
}
|
|
|
|
void QuickOpenPlugin::storeScopes( const QStringList& scopes )
|
|
{
|
|
lastUsedScopes = scopes;
|
|
KConfigGroup grp = KGlobal::config()->group("QuickOpen");
|
|
grp.writeEntry( "SelectedScopes", scopes );
|
|
}
|
|
|
|
void QuickOpenPlugin::storeItems( const QStringList& items )
|
|
{
|
|
lastUsedItems = items;
|
|
KConfigGroup grp = KGlobal::config()->group("QuickOpen");
|
|
grp.writeEntry( "SelectedItems", items );
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpen()
|
|
{
|
|
if(quickOpenLine()) //Same as clicking on Quick Open
|
|
quickOpenLine()->setFocus();
|
|
else
|
|
showQuickOpen( All );
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenFile()
|
|
{
|
|
showQuickOpen( (ModelTypes)(Files | OpenFiles) );
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenFunction()
|
|
{
|
|
showQuickOpen( Functions );
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenClass()
|
|
{
|
|
showQuickOpen( Classes );
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenOpenFile()
|
|
{
|
|
showQuickOpen( OpenFiles );
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenDocumentation()
|
|
{
|
|
showQuickOpenWidget(QStringList(i18n("Documentation")), QStringList(i18n("Includes")), true);
|
|
}
|
|
|
|
QSet<KDevelop::IndexedString> QuickOpenPlugin::fileSet() const {
|
|
return m_model->fileSet();
|
|
}
|
|
|
|
void QuickOpenPlugin::registerProvider( const QStringList& scope, const QStringList& type, KDevelop::QuickOpenDataProviderBase* provider )
|
|
{
|
|
m_model->registerProvider( scope, type, provider );
|
|
}
|
|
|
|
bool QuickOpenPlugin::removeProvider( KDevelop::QuickOpenDataProviderBase* provider )
|
|
{
|
|
m_model->removeProvider( provider );
|
|
return true;
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenDeclaration()
|
|
{
|
|
if(jumpToSpecialObject())
|
|
return;
|
|
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
Declaration* decl = cursorDeclaration();
|
|
|
|
if(!decl) {
|
|
kDebug() << "Found no declaration for cursor, cannot jump";
|
|
return;
|
|
}
|
|
decl->activateSpecialization();
|
|
|
|
IndexedString u = decl->url();
|
|
SimpleCursor c = decl->rangeInCurrentRevision().start;
|
|
|
|
if(u.str().isEmpty()) {
|
|
kDebug() << "Got empty url for declaration" << decl->toString();
|
|
return;
|
|
}
|
|
|
|
lock.unlock();
|
|
core()->documentController()->openDocument(KUrl(u.str()), c.textCursor());
|
|
}
|
|
|
|
///Returns all languages for that url that have a language support, and prints warnings for other ones.
|
|
QList<KDevelop::ILanguage*> languagesWithSupportForUrl(KUrl url) {
|
|
QList<KDevelop::ILanguage*> languages = ICore::self()->languageController()->languagesForUrl(url);
|
|
QList<KDevelop::ILanguage*> ret;
|
|
foreach( KDevelop::ILanguage* language, languages) {
|
|
if(language->languageSupport()) {
|
|
ret << language;
|
|
}else{
|
|
kDebug() << "got no language-support for language" << language->name();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QWidget* QuickOpenPlugin::specialObjectNavigationWidget() const
|
|
{
|
|
if( !ICore::self()->documentController()->activeDocument() || !ICore::self()->documentController()->activeDocument()->textDocument() || !ICore::self()->documentController()->activeDocument()->textDocument()->activeView() )
|
|
return 0;
|
|
|
|
KUrl url = ICore::self()->documentController()->activeDocument()->url();
|
|
|
|
foreach( KDevelop::ILanguage* language, languagesWithSupportForUrl(url) ) {
|
|
QWidget* w = language->languageSupport()->specialLanguageObjectNavigationWidget(url, SimpleCursor(ICore::self()->documentController()->activeDocument()->textDocument()->activeView()->cursorPosition()) );
|
|
if(w)
|
|
return w;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QPair<KUrl, SimpleCursor> QuickOpenPlugin::specialObjectJumpPosition() const {
|
|
if( !ICore::self()->documentController()->activeDocument() || !ICore::self()->documentController()->activeDocument()->textDocument() || !ICore::self()->documentController()->activeDocument()->textDocument()->activeView() )
|
|
return qMakePair(KUrl(), SimpleCursor());
|
|
|
|
KUrl url = ICore::self()->documentController()->activeDocument()->url();
|
|
|
|
foreach( KDevelop::ILanguage* language, languagesWithSupportForUrl(url) ) {
|
|
QPair<KUrl, SimpleCursor> pos = language->languageSupport()->specialLanguageObjectJumpCursor(url, SimpleCursor(ICore::self()->documentController()->activeDocument()->textDocument()->activeView()->cursorPosition()) );
|
|
if(pos.second.isValid()) {
|
|
return pos;
|
|
}
|
|
}
|
|
|
|
return qMakePair(KUrl(), SimpleCursor::invalid());
|
|
}
|
|
|
|
bool QuickOpenPlugin::jumpToSpecialObject()
|
|
{
|
|
QPair<KUrl, SimpleCursor> pos = specialObjectJumpPosition();
|
|
if(pos.second.isValid()) {
|
|
if(pos.first.isEmpty()) {
|
|
kDebug() << "Got empty url for special language object";
|
|
return false;
|
|
}
|
|
|
|
ICore::self()->documentController()->openDocument(pos.first, pos.second.textCursor());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void QuickOpenPlugin::quickOpenDefinition()
|
|
{
|
|
if(jumpToSpecialObject())
|
|
return;
|
|
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
Declaration* decl = cursorDeclaration();
|
|
|
|
if(!decl) {
|
|
kDebug() << "Found no declaration for cursor, cannot jump";
|
|
return;
|
|
}
|
|
|
|
IndexedString u = decl->url();
|
|
SimpleCursor c = decl->rangeInCurrentRevision().start;
|
|
if(FunctionDefinition* def = FunctionDefinition::definition(decl)) {
|
|
def->activateSpecialization();
|
|
u = def->url();
|
|
c = def->rangeInCurrentRevision().start;
|
|
}else{
|
|
kDebug() << "Found no definition for declaration";
|
|
decl->activateSpecialization();
|
|
}
|
|
|
|
if(u.str().isEmpty()) {
|
|
kDebug() << "Got empty url for declaration" << decl->toString();
|
|
return;
|
|
}
|
|
|
|
lock.unlock();
|
|
core()->documentController()->openDocument(KUrl(u.str()), c.textCursor());
|
|
}
|
|
|
|
bool QuickOpenPlugin::freeModel()
|
|
{
|
|
if(m_currentWidgetHandler)
|
|
delete m_currentWidgetHandler;
|
|
m_currentWidgetHandler = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void QuickOpenPlugin::nextFunction()
|
|
{
|
|
jumpToNearestFunction(NextFunction);
|
|
}
|
|
|
|
void QuickOpenPlugin::previousFunction()
|
|
{
|
|
jumpToNearestFunction(PreviousFunction);
|
|
}
|
|
|
|
void QuickOpenPlugin::jumpToNearestFunction(QuickOpenPlugin::FunctionJumpDirection direction)
|
|
{
|
|
IDocument* doc = ICore::self()->documentController()->activeDocument();
|
|
if(!doc) {
|
|
kDebug() << "No active document";
|
|
return;
|
|
}
|
|
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
|
|
TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() );
|
|
|
|
if( !context ) {
|
|
kDebug() << "Got no standard context";
|
|
return;
|
|
}
|
|
|
|
QList<DUChainItem> items;
|
|
OutlineFilter filter(items, OutlineFilter::Functions);
|
|
DUChainUtils::collectItems( context, filter );
|
|
|
|
CursorInRevision cursor = context->transformToLocalRevision(SimpleCursor(doc->cursorPosition()));
|
|
if (!cursor.isValid())
|
|
return;
|
|
|
|
Declaration *nearestDeclBefore = 0;
|
|
int distanceBefore = INT_MIN;
|
|
Declaration *nearestDeclAfter = 0;
|
|
int distanceAfter = INT_MAX;
|
|
|
|
for (int i = 0; i < items.count(); ++i) {
|
|
Declaration *decl = items[i].m_item.data();
|
|
|
|
int distance = decl->range().start.line - cursor.line;
|
|
if (distance < 0 && distance >= distanceBefore) {
|
|
distanceBefore = distance;
|
|
nearestDeclBefore = decl;
|
|
} else if (distance > 0 && distance <= distanceAfter) {
|
|
distanceAfter = distance;
|
|
nearestDeclAfter = decl;
|
|
}
|
|
}
|
|
|
|
CursorInRevision c = CursorInRevision::invalid();
|
|
if (direction == QuickOpenPlugin::NextFunction && nearestDeclAfter)
|
|
c = nearestDeclAfter->range().start;
|
|
else if (direction == QuickOpenPlugin::PreviousFunction && nearestDeclBefore)
|
|
c = nearestDeclBefore->range().start;
|
|
|
|
KTextEditor::Cursor textCursor = KTextEditor::Cursor::invalid();
|
|
if (c.isValid())
|
|
textCursor = context->transformFromLocalRevision(c).textCursor();
|
|
|
|
lock.unlock();
|
|
if (textCursor.isValid())
|
|
core()->documentController()->openDocument(doc->url(), textCursor);
|
|
else
|
|
kDebug() << "No declaration to jump to";
|
|
}
|
|
|
|
|
|
struct CreateOutlineDialog {
|
|
CreateOutlineDialog() : dialog(0), cursorDecl(0), model(0) {
|
|
}
|
|
|
|
void start() {
|
|
if(!QuickOpenPlugin::self()->freeModel())
|
|
return;
|
|
|
|
IDocument* doc = ICore::self()->documentController()->activeDocument();
|
|
if(!doc) {
|
|
kDebug() << "No active document";
|
|
return;
|
|
}
|
|
|
|
KDevelop::DUChainReadLocker lock( DUChain::lock() );
|
|
|
|
TopDUContext* context = DUChainUtils::standardContextForUrl( doc->url() );
|
|
|
|
if( !context ) {
|
|
kDebug() << "Got no standard context";
|
|
return;
|
|
}
|
|
|
|
model = new QuickOpenModel(0);
|
|
|
|
OutlineFilter filter(items);
|
|
|
|
DUChainUtils::collectItems( context, filter );
|
|
|
|
if(noHtmlDestriptionInOutline) {
|
|
for(int a = 0; a < items.size(); ++a)
|
|
items[a].m_noHtmlDestription = true;
|
|
}
|
|
|
|
cursorDecl = cursorContextDeclaration();
|
|
|
|
model->registerProvider( QStringList(), QStringList(), new DeclarationListDataProvider(QuickOpenPlugin::self(), items, true) );
|
|
|
|
dialog = new QuickOpenWidgetDialog( i18n("Outline"), model, QStringList(), QStringList(), true );
|
|
|
|
model->setParent(dialog->widget());
|
|
}
|
|
void finish() {
|
|
//Select the declaration that contains the cursor
|
|
if(cursorDecl && dialog) {
|
|
int num = 0;
|
|
foreach(const DUChainItem& item, items) {
|
|
if(item.m_item.data() == cursorDecl) {
|
|
dialog->widget()->o.list->setCurrentIndex( model->index(num,0,QModelIndex()) );
|
|
dialog->widget()->o.list->scrollTo( model->index(num,0,QModelIndex()), QAbstractItemView::PositionAtCenter );
|
|
}
|
|
++num;
|
|
}
|
|
}
|
|
}
|
|
QPointer<QuickOpenWidgetDialog> dialog;
|
|
Declaration* cursorDecl;
|
|
QList<DUChainItem> items;
|
|
QuickOpenModel* model;
|
|
};
|
|
|
|
class OutlineQuickopenWidgetCreator : public QuickOpenWidgetCreator {
|
|
public:
|
|
OutlineQuickopenWidgetCreator(QStringList /*scopes*/, QStringList /*items*/) : m_creator(0) {
|
|
}
|
|
|
|
~OutlineQuickopenWidgetCreator() {
|
|
delete m_creator;
|
|
}
|
|
|
|
virtual QuickOpenWidget* createWidget() {
|
|
delete m_creator;
|
|
m_creator = new CreateOutlineDialog;
|
|
m_creator->start();
|
|
|
|
if(!m_creator->dialog)
|
|
return 0;
|
|
|
|
m_creator->dialog->deleteLater();
|
|
return m_creator->dialog->widget();
|
|
}
|
|
|
|
virtual void widgetShown() {
|
|
if(m_creator) {
|
|
m_creator->finish();
|
|
delete m_creator;
|
|
m_creator = 0;
|
|
}
|
|
}
|
|
|
|
virtual QString objectNameForLine() {
|
|
return "Outline";
|
|
}
|
|
|
|
CreateOutlineDialog* m_creator;
|
|
};
|
|
|
|
void QuickOpenPlugin::quickOpenNavigateFunctions()
|
|
{
|
|
CreateOutlineDialog create;
|
|
create.start();
|
|
|
|
if(!create.dialog)
|
|
return;
|
|
|
|
m_currentWidgetHandler = create.dialog;
|
|
|
|
QuickOpenLineEdit* line = quickOpenLine("Outline");
|
|
if(!line)
|
|
line = quickOpenLine();
|
|
|
|
if(line) {
|
|
line->showWithWidget(create.dialog->widget());
|
|
create.dialog->deleteLater();
|
|
}else
|
|
create.dialog->run();
|
|
|
|
create.finish();
|
|
}
|
|
|
|
QuickOpenLineEdit::QuickOpenLineEdit(QuickOpenWidgetCreator* creator) : m_widget(0), m_forceUpdate(false), m_widgetCreator(creator) {
|
|
setMinimumWidth(200);
|
|
setMaximumWidth(400);
|
|
|
|
deactivate();
|
|
setDefaultText(i18n("Quick Open..."));
|
|
setToolTip(i18n("Search for files, classes, functions and more,"
|
|
" allowing you to quickly navigate in your source code."));
|
|
setObjectName(m_widgetCreator->objectNameForLine());
|
|
setFocusPolicy(Qt::ClickFocus);
|
|
}
|
|
|
|
QuickOpenLineEdit::~QuickOpenLineEdit() {
|
|
delete m_widget;
|
|
delete m_widgetCreator;
|
|
}
|
|
|
|
bool QuickOpenLineEdit::insideThis(QObject* object) {
|
|
while (object)
|
|
{
|
|
kDebug() << object;
|
|
if (object == this || object == m_widget)
|
|
{
|
|
return true;
|
|
}
|
|
object = object->parent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void QuickOpenLineEdit::widgetDestroyed(QObject* obj)
|
|
{
|
|
Q_UNUSED(obj);
|
|
deactivate();
|
|
}
|
|
|
|
void QuickOpenLineEdit::showWithWidget(QuickOpenWidget* widget)
|
|
{
|
|
connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
|
|
kDebug() << "storing widget" << widget;
|
|
deactivate();
|
|
if(m_widget) {
|
|
kDebug() << "deleting" << m_widget;
|
|
delete m_widget;
|
|
}
|
|
m_widget = widget;
|
|
m_forceUpdate = true;
|
|
setFocus();
|
|
}
|
|
|
|
void QuickOpenLineEdit::focusInEvent(QFocusEvent* ev) {
|
|
QLineEdit::focusInEvent(ev);
|
|
// delete m_widget;
|
|
kDebug() << "got focus";
|
|
kDebug() << "old widget" << m_widget << "force update:" << m_forceUpdate;
|
|
if (m_widget && !m_forceUpdate)
|
|
return;
|
|
|
|
if (!m_forceUpdate && !QuickOpenPlugin::self()->freeModel()) {
|
|
deactivate();
|
|
return;
|
|
}
|
|
|
|
m_forceUpdate = false;
|
|
|
|
if(!m_widget)
|
|
{
|
|
m_widget = m_widgetCreator->createWidget();
|
|
if(!m_widget) {
|
|
deactivate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
activate();
|
|
|
|
m_widget->showStandardButtons(false);
|
|
m_widget->showSearchField(false);
|
|
|
|
m_widget->setParent(0, Qt::ToolTip);
|
|
m_widget->setFocusPolicy(Qt::NoFocus);
|
|
m_widget->setAlternativeSearchField(this);
|
|
|
|
QuickOpenPlugin::self()->m_currentWidgetHandler = m_widget;
|
|
connect(m_widget, SIGNAL(ready()), SLOT(deactivate()));
|
|
|
|
connect( m_widget, SIGNAL(scopesChanged(QStringList)), QuickOpenPlugin::self(), SLOT(storeScopes(QStringList)) );
|
|
connect( m_widget, SIGNAL(itemsChanged(QStringList)), QuickOpenPlugin::self(), SLOT(storeItems(QStringList)) );
|
|
Q_ASSERT(m_widget->o.searchLine == this);
|
|
m_widget->prepareShow();
|
|
QRect widgetGeometry = QRect(mapToGlobal(QPoint(0, height())), mapToGlobal(QPoint(width(), height() + 400)));
|
|
widgetGeometry.setWidth(700); ///@todo Waste less space
|
|
QRect screenGeom = QApplication::desktop()->screenGeometry(this);
|
|
if (widgetGeometry.right() > screenGeom.right()) {
|
|
widgetGeometry.moveRight(screenGeom.right());
|
|
}
|
|
if (widgetGeometry.bottom() > screenGeom.bottom()) {
|
|
widgetGeometry.moveBottom(mapToGlobal(QPoint(0, 0)).y());
|
|
}
|
|
m_widget->setGeometry(widgetGeometry);
|
|
m_widget->show();
|
|
|
|
m_widgetCreator->widgetShown();
|
|
}
|
|
|
|
void QuickOpenLineEdit::hideEvent(QHideEvent* ev)
|
|
{
|
|
QWidget::hideEvent(ev);
|
|
if(m_widget)
|
|
QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection);
|
|
// deactivate();
|
|
}
|
|
|
|
bool QuickOpenLineEdit::eventFilter(QObject* obj, QEvent* e) {
|
|
if (!m_widget)
|
|
return false;
|
|
|
|
switch (e->type()) {
|
|
case QEvent::KeyPress:
|
|
case QEvent::ShortcutOverride:
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
|
|
deactivate();
|
|
e->accept();
|
|
return true;
|
|
}
|
|
break;
|
|
case QEvent::WindowActivate:
|
|
case QEvent::WindowDeactivate:
|
|
kDebug() << "closing because of window activation";
|
|
deactivate();
|
|
break;
|
|
// handle bug 260657 - "Outline menu doesn't follow main window on its move"
|
|
case QEvent::Move: {
|
|
QWidget* widget = qobject_cast<QWidget*>(obj);
|
|
Q_ASSERT(widget);
|
|
// close the outline menu in case a parent widget moved
|
|
if (widget->isAncestorOf(this)) {
|
|
kDebug() << "closing because of parent widget move";
|
|
deactivate();
|
|
}
|
|
break;
|
|
}
|
|
case QEvent::FocusIn:
|
|
if (dynamic_cast<QWidget*>(obj)) {
|
|
QFocusEvent* focusEvent = dynamic_cast<QFocusEvent*>(e);
|
|
Q_ASSERT(focusEvent);
|
|
//Eat the focus event, keep the focus
|
|
kDebug() << "focus change" << "inside this: " << insideThis(obj) << "this" << this << "obj" << obj;
|
|
if(obj == this)
|
|
return false;
|
|
|
|
kDebug() << "reason" << focusEvent->reason();
|
|
if (focusEvent->reason() != Qt::MouseFocusReason && focusEvent->reason() != Qt::ActiveWindowFocusReason) {
|
|
QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection);
|
|
return false;
|
|
}
|
|
if (!insideThis(obj))
|
|
deactivate();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
void QuickOpenLineEdit::activate() {
|
|
kDebug() << "activating";
|
|
setText("");
|
|
setStyleSheet("");
|
|
qApp->installEventFilter(this);
|
|
}
|
|
void QuickOpenLineEdit::deactivate() {
|
|
kDebug() << "deactivating";
|
|
|
|
clear();
|
|
|
|
if(m_widget || hasFocus())
|
|
QMetaObject::invokeMethod(this, "checkFocus", Qt::QueuedConnection);
|
|
|
|
if (m_widget)
|
|
m_widget->deleteLater();
|
|
|
|
m_widget = 0;
|
|
qApp->removeEventFilter(this);
|
|
|
|
}
|
|
|
|
void QuickOpenLineEdit::checkFocus()
|
|
{
|
|
kDebug() << "checking focus" << m_widget;
|
|
if(m_widget) {
|
|
if(isVisible() && !isHidden())
|
|
setFocus();
|
|
else
|
|
deactivate();
|
|
}else{
|
|
if (ICore::self()->documentController()->activeDocument())
|
|
ICore::self()->documentController()->activateDocument(ICore::self()->documentController()->activeDocument());
|
|
|
|
//Make sure the focus is somewehre else, even if there is no active document
|
|
setEnabled(false);
|
|
setEnabled(true);
|
|
}
|
|
}
|
|
|
|
IQuickOpenLine* QuickOpenPlugin::createQuickOpenLine(const QStringList& scopes, const QStringList& type, IQuickOpen::QuickOpenType kind)
|
|
{
|
|
if(kind == Outline)
|
|
return new QuickOpenLineEdit(new OutlineQuickopenWidgetCreator(scopes, type));
|
|
else
|
|
return new QuickOpenLineEdit(new StandardQuickOpenWidgetCreator(scopes, type));
|
|
}
|
|
|
|
#include "moc_quickopenplugin.cpp"
|
|
|
|
// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on
|