mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
433 lines
13 KiB
C++
433 lines
13 KiB
C++
/* This file is part of the KDE libraries
|
|
Copyright (C) 2007 David Nolden <david.nolden.kdevelop@art-master.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "quickopenmodel.h"
|
|
|
|
#include <QTreeView>
|
|
#include <QTimer>
|
|
|
|
#include <ktexteditor/codecompletionmodel.h>
|
|
#include <kdebug.h>
|
|
#include <typeinfo>
|
|
|
|
#define QUICKOPEN_USE_ITEM_CACHING
|
|
|
|
using namespace KDevelop;
|
|
|
|
QuickOpenModel::QuickOpenModel( QWidget* parent ) : ExpandingWidgetModel( parent ), m_treeView(0), m_expandingWidgetHeightIncrease(0), m_resetBehindRow(0)
|
|
{
|
|
m_resetTimer = new QTimer(this);
|
|
m_resetTimer->setSingleShot(true);
|
|
connect(m_resetTimer, SIGNAL(timeout()), this, SLOT(resetTimer()));
|
|
}
|
|
|
|
void QuickOpenModel::setExpandingWidgetHeightIncrease(int pixels)
|
|
{
|
|
m_expandingWidgetHeightIncrease = pixels;
|
|
}
|
|
|
|
QStringList QuickOpenModel::allScopes() const
|
|
{
|
|
QStringList scopes;
|
|
foreach( const ProviderEntry& provider, m_providers )
|
|
foreach( const QString& scope, provider.scopes )
|
|
if( !scopes.contains( scope ) )
|
|
scopes << scope;
|
|
|
|
return scopes;
|
|
}
|
|
|
|
QStringList QuickOpenModel::allTypes() const
|
|
{
|
|
QSet<QString> types;
|
|
foreach( const ProviderEntry& provider, m_providers )
|
|
types += provider.types;
|
|
|
|
return types.toList();
|
|
}
|
|
|
|
void QuickOpenModel::registerProvider( const QStringList& scopes, const QStringList& types, KDevelop::QuickOpenDataProviderBase* provider )
|
|
{
|
|
ProviderEntry e;
|
|
e.scopes = QSet<QString>::fromList(scopes);
|
|
e.types = QSet<QString>::fromList(types);
|
|
e.provider = provider;
|
|
|
|
m_providers << e; //.insert( types, e );
|
|
|
|
connect( provider, SIGNAL(destroyed(QObject*)), this, SLOT(destroyed(QObject*)) );
|
|
|
|
restart(true);
|
|
}
|
|
|
|
bool QuickOpenModel::removeProvider( KDevelop::QuickOpenDataProviderBase* provider )
|
|
{
|
|
bool ret = false;
|
|
for( ProviderList::iterator it = m_providers.begin(); it != m_providers.end(); ++it ) {
|
|
if( (*it).provider == provider ) {
|
|
m_providers.erase( it );
|
|
disconnect( provider, SIGNAL(destroyed(QObject*)), this, SLOT(destroyed(QObject*)) );
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
restart(true);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void QuickOpenModel::enableProviders( const QStringList& _items, const QStringList& _scopes )
|
|
{
|
|
QSet<QString> items = QSet<QString>::fromList( _items );
|
|
QSet<QString> scopes = QSet<QString>::fromList( _scopes );
|
|
if (m_enabledItems == items && m_enabledScopes == scopes && !items.isEmpty() && !scopes.isEmpty()) {
|
|
return;
|
|
}
|
|
m_enabledItems = items;
|
|
m_enabledScopes = scopes;
|
|
kDebug() << "params " << items << " " << scopes;
|
|
|
|
//We use 2 iterations here: In the first iteration, all providers that implement QuickOpenFileSetInterface are initialized, then the other ones.
|
|
//The reason is that the second group can refer to the first one.
|
|
for( ProviderList::iterator it = m_providers.begin(); it != m_providers.end(); ++it ) {
|
|
if( !dynamic_cast<QuickOpenFileSetInterface*>((*it).provider) )
|
|
continue;
|
|
kDebug() << "comparing" << (*it).scopes << (*it).types;
|
|
if( ( scopes.isEmpty() || !( scopes & (*it).scopes ).isEmpty() ) && ( !( items & (*it).types ).isEmpty() || items.isEmpty() ) ) {
|
|
kDebug() << "enabling " << (*it).types << " " << (*it).scopes;
|
|
(*it).enabled = true;
|
|
(*it).provider->enableData( _items, _scopes );
|
|
} else {
|
|
kDebug() << "disabling " << (*it).types << " " << (*it).scopes;
|
|
(*it).enabled = false;
|
|
if( ( scopes.isEmpty() || !( scopes & (*it).scopes ).isEmpty() ) )
|
|
(*it).provider->enableData( _items, _scopes ); //The provider may still provide files
|
|
}
|
|
}
|
|
|
|
for( ProviderList::iterator it = m_providers.begin(); it != m_providers.end(); ++it ) {
|
|
if( dynamic_cast<QuickOpenFileSetInterface*>((*it).provider) )
|
|
continue;
|
|
kDebug() << "comparing" << (*it).scopes << (*it).types;
|
|
if( ( scopes.isEmpty() || !( scopes & (*it).scopes ).isEmpty() ) && ( !( items & (*it).types ).isEmpty() || items.isEmpty() ) ) {
|
|
kDebug() << "enabling " << (*it).types << " " << (*it).scopes;
|
|
(*it).enabled = true;
|
|
(*it).provider->enableData( _items, _scopes );
|
|
} else {
|
|
kDebug() << "disabling " << (*it).types << " " << (*it).scopes;
|
|
(*it).enabled = false;
|
|
}
|
|
}
|
|
|
|
restart(true);
|
|
}
|
|
|
|
void QuickOpenModel::textChanged( const QString& str )
|
|
{
|
|
if( m_filterText == str )
|
|
return;
|
|
m_filterText = str;
|
|
foreach( const ProviderEntry& provider, m_providers )
|
|
if( provider.enabled )
|
|
provider.provider->setFilterText( str );
|
|
|
|
m_cachedData.clear();
|
|
clearExpanding();
|
|
|
|
//Get the 50 first items, so the data-providers notice changes without ui-glitches due to resetting
|
|
for(int a = 0; a < 50 && a < rowCount(QModelIndex()) ; ++a)
|
|
getItem(a, true);
|
|
|
|
|
|
reset();
|
|
}
|
|
|
|
void QuickOpenModel::restart(bool keepFilterText)
|
|
{
|
|
// make sure we do not restart recursivly which could lead to
|
|
// recursive loading of provider plugins e.g. (happened for the cpp plugin)
|
|
QMetaObject::invokeMethod(this, "restart_internal", Qt::QueuedConnection,
|
|
Q_ARG(bool, keepFilterText));
|
|
}
|
|
|
|
void QuickOpenModel::restart_internal(bool keepFilterText)
|
|
{
|
|
if(!keepFilterText)
|
|
m_filterText.clear();
|
|
|
|
bool anyEnabled = false;
|
|
|
|
foreach( const ProviderEntry& e, m_providers )
|
|
anyEnabled |= e.enabled;
|
|
|
|
if( !anyEnabled )
|
|
return;
|
|
|
|
foreach( const ProviderEntry& provider, m_providers ) {
|
|
if( !dynamic_cast<QuickOpenFileSetInterface*>(provider.provider) )
|
|
continue;
|
|
|
|
///Always reset providers that implement QuickOpenFileSetInterface and have some matchign scopes, because they may be needed by other providers.
|
|
if( m_enabledScopes.isEmpty() || !( m_enabledScopes & provider.scopes ).isEmpty() )
|
|
provider.provider->reset();
|
|
}
|
|
foreach( const ProviderEntry& provider, m_providers ) {
|
|
if( dynamic_cast<QuickOpenFileSetInterface*>(provider.provider) )
|
|
continue;
|
|
|
|
if( provider.enabled && provider.provider )
|
|
provider.provider->reset();
|
|
}
|
|
|
|
if(keepFilterText) {
|
|
textChanged(m_filterText);
|
|
}else{
|
|
m_cachedData.clear();
|
|
clearExpanding();
|
|
|
|
reset();
|
|
}
|
|
}
|
|
|
|
void QuickOpenModel::destroyed( QObject* obj )
|
|
{
|
|
removeProvider( static_cast<KDevelop::QuickOpenDataProviderBase*>(obj) );
|
|
}
|
|
|
|
QModelIndex QuickOpenModel::index( int row, int column, const QModelIndex& /*parent*/) const
|
|
{
|
|
if( column >= columnCount() || row >= rowCount(QModelIndex()) )
|
|
return QModelIndex();
|
|
return createIndex( row, column );
|
|
}
|
|
|
|
QModelIndex QuickOpenModel::parent( const QModelIndex& ) const
|
|
{
|
|
return QModelIndex();
|
|
}
|
|
|
|
int QuickOpenModel::rowCount( const QModelIndex& i ) const
|
|
{
|
|
if( i.isValid() )
|
|
return 0;
|
|
|
|
int count = 0;
|
|
foreach( const ProviderEntry& provider, m_providers )
|
|
if( provider.enabled )
|
|
count += provider.provider->itemCount();
|
|
|
|
return count;
|
|
}
|
|
|
|
int QuickOpenModel::unfilteredRowCount() const
|
|
{
|
|
int count = 0;
|
|
foreach( const ProviderEntry& provider, m_providers )
|
|
if( provider.enabled )
|
|
count += provider.provider->unfilteredItemCount();
|
|
|
|
return count;
|
|
}
|
|
|
|
int QuickOpenModel::columnCount() const
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
int QuickOpenModel::columnCount( const QModelIndex& index ) const
|
|
{
|
|
if( index.parent().isValid() )
|
|
return 0;
|
|
else {
|
|
return columnCount();
|
|
}
|
|
}
|
|
|
|
QVariant QuickOpenModel::data( const QModelIndex& index, int role ) const
|
|
{
|
|
QuickOpenDataPointer d = getItem( index.row() );
|
|
|
|
if( !d )
|
|
return QVariant();
|
|
|
|
switch( role ) {
|
|
case KTextEditor::CodeCompletionModel::ItemSelected: {
|
|
QString desc = d->htmlDescription();
|
|
if(desc.isEmpty())
|
|
return QVariant();
|
|
else
|
|
return desc;
|
|
}
|
|
|
|
case KTextEditor::CodeCompletionModel::IsExpandable:
|
|
return d->isExpandable();
|
|
case KTextEditor::CodeCompletionModel::ExpandingWidget: {
|
|
QVariant v;
|
|
QWidget* w = d->expandingWidget();
|
|
if(w && m_expandingWidgetHeightIncrease)
|
|
w->resize(w->width(), w->height() + m_expandingWidgetHeightIncrease);
|
|
|
|
v.setValue<QWidget*>(w);
|
|
return v;
|
|
}
|
|
}
|
|
|
|
if( index.column() == 1 )
|
|
{ //This column contains the actual content
|
|
switch( role ) {
|
|
case Qt::DecorationRole:
|
|
return d->icon();
|
|
|
|
case Qt::DisplayRole:
|
|
return d->text();
|
|
case KTextEditor::CodeCompletionModel::HighlightingMethod:
|
|
return KTextEditor::CodeCompletionModel::CustomHighlighting;
|
|
case KTextEditor::CodeCompletionModel::CustomHighlight:
|
|
return d->highlighting();
|
|
}
|
|
} else if( index.column() == 0 )
|
|
{ //This column only contains the expanded/not expanded icon
|
|
switch( role ) {
|
|
case Qt::DecorationRole:
|
|
{
|
|
if( isExpandable(index) ) {
|
|
//Show the expanded/unexpanded handles
|
|
cacheIcons();
|
|
if( isExpanded(index) ) {
|
|
return m_expandedIcon;
|
|
} else {
|
|
return m_collapsedIcon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ExpandingWidgetModel::data( index, role );
|
|
}
|
|
|
|
void QuickOpenModel::resetTimer() {
|
|
|
|
//Remove all cached data behind row m_resetBehindRow
|
|
for(DataList::iterator it = m_cachedData.begin(); it != m_cachedData.end(); ) {
|
|
if(it.key() > m_resetBehindRow)
|
|
it = m_cachedData.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
|
|
if(treeView()) {
|
|
QModelIndex currentIndex(treeView()->currentIndex());
|
|
QAbstractItemModel::reset(); //New items have been inserted
|
|
if (currentIndex.isValid()) {
|
|
treeView()->setCurrentIndex(index(currentIndex.row(), 0, QModelIndex())); //Preserve the current index
|
|
}
|
|
}
|
|
m_resetBehindRow = 0;
|
|
}
|
|
|
|
QuickOpenDataPointer QuickOpenModel::getItem( int row, bool noReset ) const {
|
|
///@todo mix all the models alphabetically here. For now, they are simply ordered.
|
|
///@todo Deal with unexpected item-counts, like for example in the case of overloaded function-declarations
|
|
|
|
#ifdef QUICKOPEN_USE_ITEM_CACHING
|
|
if( m_cachedData.contains( row ) )
|
|
return m_cachedData[row];
|
|
#endif
|
|
int rowOffset = 0;
|
|
|
|
Q_ASSERT(row < rowCount(QModelIndex()));
|
|
|
|
foreach( const ProviderEntry& provider, m_providers ) {
|
|
if( !provider.enabled )
|
|
continue;
|
|
uint itemCount = provider.provider->itemCount();
|
|
if( (uint)row < itemCount )
|
|
{
|
|
QuickOpenDataPointer item = provider.provider->data( row );
|
|
|
|
if(!noReset && provider.provider->itemCount() != itemCount) {
|
|
kDebug() << "item-count in provider has changed, resetting model";
|
|
m_resetTimer->start(0);
|
|
m_resetBehindRow = rowOffset + row; //Don't reset everything, only everything behind this position
|
|
}
|
|
|
|
#ifdef QUICKOPEN_USE_ITEM_CACHING
|
|
m_cachedData[row+rowOffset] = item;
|
|
#endif
|
|
return item;
|
|
} else {
|
|
row -= provider.provider->itemCount();
|
|
rowOffset += provider.provider->itemCount();
|
|
}
|
|
}
|
|
|
|
// kWarning() << "No item for row " << row;
|
|
|
|
return QuickOpenDataPointer();
|
|
}
|
|
|
|
QSet<IndexedString> QuickOpenModel::fileSet() const {
|
|
QSet<IndexedString> merged;
|
|
foreach( const ProviderEntry& provider, m_providers ) {
|
|
if( m_enabledScopes.isEmpty() || !( m_enabledScopes & provider.scopes ).isEmpty() ) {
|
|
if( QuickOpenFileSetInterface* iface = dynamic_cast<QuickOpenFileSetInterface*>(provider.provider) ) {
|
|
QSet<IndexedString> ifiles = iface->files();
|
|
//kDebug() << "got file-list with" << ifiles.count() << "entries from data-provider" << typeid(*iface).name();
|
|
merged += ifiles;
|
|
}
|
|
}
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
QTreeView* QuickOpenModel::treeView() const {
|
|
return m_treeView;
|
|
}
|
|
|
|
bool QuickOpenModel::indexIsItem(const QModelIndex& /*index*/) const {
|
|
return true;
|
|
}
|
|
|
|
void QuickOpenModel::setTreeView( QTreeView* view ) {
|
|
m_treeView = view;
|
|
}
|
|
|
|
int QuickOpenModel::contextMatchQuality(const QModelIndex & /*index*/) const {
|
|
return -1;
|
|
}
|
|
|
|
bool QuickOpenModel::execute( const QModelIndex& index, QString& filterText )
|
|
{
|
|
kDebug() << "executing model";
|
|
if( !index.isValid() ) {
|
|
kWarning() << "Invalid index executed";
|
|
return false;
|
|
}
|
|
|
|
QuickOpenDataPointer item = getItem( index.row() );
|
|
|
|
if( item ) {
|
|
return item->execute( filterText );
|
|
}else{
|
|
kWarning() << "Got no item for row " << index.row() << " ";
|
|
}
|
|
|
|
return false;
|
|
}
|