diff --git a/plugins/quickopen/duchainitemquickopen.cpp b/plugins/quickopen/duchainitemquickopen.cpp index de3095041e..139104bd35 100644 --- a/plugins/quickopen/duchainitemquickopen.cpp +++ b/plugins/quickopen/duchainitemquickopen.cpp @@ -1,247 +1,252 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden 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 "projectitemquickopen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; DUChainItemData::DUChainItemData( const DUChainItem& file, bool openDefinition ) : m_item(file) , m_openDefinition(openDefinition) { } QString DUChainItemData::text() const { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) return i18n("Not available any more: %1", m_item.m_text); if(FunctionDefinition* def = dynamic_cast(decl)) { if(def->declaration()) { decl = def->declaration(); } } QString text = decl->qualifiedIdentifier().toString(); if(!decl->abstractType()) { //With simplified representation, still mark functions as such by adding parens if(dynamic_cast(decl)) { text += QLatin1String("(...)"); } }else if(TypePtr function = decl->type()) { text += function->partToString( FunctionType::SignatureArguments ); } return text; } QList DUChainItemData::highlighting() const { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return QList(); } if(FunctionDefinition* def = dynamic_cast(decl)) { if(def->declaration()) { decl = def->declaration(); } } QTextCharFormat boldFormat; boldFormat.setFontWeight(QFont::Bold); QTextCharFormat normalFormat; int prefixLength = 0; QString signature; TypePtr function = decl->type(); if(function) { signature = function->partToString( FunctionType::SignatureArguments ); } //Only highlight the last part of the qualified identifier, so the scope doesn't distract too much QualifiedIdentifier id = decl->qualifiedIdentifier(); QString fullId = id.toString(); QString lastId; if(!id.isEmpty()) { lastId = id.last().toString(); } prefixLength += fullId.length() - lastId.length(); QList ret; ret << 0; ret << prefixLength; ret << QVariant(normalFormat); ret << prefixLength; ret << lastId.length(); ret << QVariant(boldFormat); if(!signature.isEmpty()) { ret << prefixLength + lastId.length(); ret << signature.length(); ret << QVariant(normalFormat); } return ret; } QString DUChainItemData::htmlDescription() const { if(m_item.m_noHtmlDestription) { return QString(); } DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return i18n("Not available any more"); } TypePtr function = decl->type(); QString text; if( function && function->returnType() ) { text = i18nc("%1: function signature", "Return: %1", function->partToString(FunctionType::SignatureReturn)) + QLatin1String(" "); } text += i18nc("%1: file path", "File: %1", ICore::self()->projectController()->prettyFileName(decl->url().toUrl())); QString ret = "" + text + ""; return ret; } bool DUChainItemData::execute( QString& /*filterText*/ ) { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return false; } if(m_openDefinition && FunctionDefinition::definition(decl)) { decl = FunctionDefinition::definition(decl); } QUrl url = decl->url().toUrl(); KTextEditor::Cursor cursor = decl->rangeInCurrentRevision().start(); DUContext* internal = decl->internalContext(); if(internal && (internal->type() == DUContext::Other || internal->type() == DUContext::Class)) { //Move into the body if(internal->range().end.line > internal->range().start.line) { cursor = KTextEditor::Cursor(internal->range().start.line+1, 0); //Move into the body } } lock.unlock(); ICore::self()->documentController()->openDocument( url, cursor ); return true; } bool DUChainItemData::isExpandable() const { return true; } QWidget* DUChainItemData::expandingWidget() const { DUChainReadLocker lock;; Declaration* decl = dynamic_cast(m_item.m_item.data()); if( !decl || !decl->context() ) { return 0; } return decl->context()->createNavigationWidget( decl, decl->topContext(), QString(), QString(), AbstractNavigationWidget::EmbeddableWidget); } QIcon DUChainItemData::icon() const { return QIcon(); } +Path DUChainItemData::projectPath() const +{ + return m_item.m_projectPath; +} + DUChainItemDataProvider::DUChainItemDataProvider( IQuickOpen* quickopen, bool openDefinitions ) : m_quickopen(quickopen) , m_openDefinitions(openDefinitions) { reset(); } void DUChainItemDataProvider::setFilterText( const QString& text ) { Base::setFilter( text ); } uint DUChainItemDataProvider::itemCount() const { return Base::filteredItems().count(); } uint DUChainItemDataProvider::unfilteredItemCount() const { return Base::items().count(); } QuickOpenDataPointer DUChainItemDataProvider::data( uint row ) const { return KDevelop::QuickOpenDataPointer( createData( Base::filteredItems()[row] ) ); } DUChainItemData* DUChainItemDataProvider::createData( const DUChainItem& item ) const { return new DUChainItemData( item, m_openDefinitions ); } QString DUChainItemDataProvider::itemText( const DUChainItem& data ) const { return data.m_text; } void DUChainItemDataProvider::reset() { } diff --git a/plugins/quickopen/duchainitemquickopen.h b/plugins/quickopen/duchainitemquickopen.h index cba333e836..73eb1f96eb 100644 --- a/plugins/quickopen/duchainitemquickopen.h +++ b/plugins/quickopen/duchainitemquickopen.h @@ -1,102 +1,104 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden 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. */ #ifndef DUCHAIN_ITEM_QUICKOPEN #define DUCHAIN_ITEM_QUICKOPEN #include #include #include #include namespace KDevelop { class IQuickOpen; } struct DUChainItem { DUChainItem() : m_noHtmlDestription(false) { } KDevelop::IndexedDeclaration m_item; QString m_text; + KDevelop::Path m_projectPath; bool m_noHtmlDestription; }; Q_DECLARE_TYPEINFO(DUChainItem, Q_MOVABLE_TYPE); class DUChainItemData : public KDevelop::QuickOpenDataBase { public: explicit DUChainItemData( const DUChainItem& item, bool openDefinition = false ); QString text() const override; QString htmlDescription() const override; QList highlighting() const override; bool execute( QString& filterText ) override; bool isExpandable() const override; QWidget* expandingWidget() const override; QIcon icon() const override; + KDevelop::Path projectPath() const; private: DUChainItem m_item; bool m_openDefinition; }; /** * A QuickOpenDataProvider that presents a list of declarations. * The declarations need to be set using setItems(..) in a re-implemented reset() function. * */ class DUChainItemDataProvider : public KDevelop::QuickOpenDataProviderBase, public KDevelop::Filter { Q_OBJECT public: typedef KDevelop::Filter Base; /// When openDefinitions is true, the definitions will be opened if available on execute(). explicit DUChainItemDataProvider( KDevelop::IQuickOpen* quickopen, bool openDefinitions = false ); void setFilterText( const QString& text ) override; uint itemCount() const override; uint unfilteredItemCount() const override; KDevelop::QuickOpenDataPointer data( uint row ) const override; void reset() override; protected: //Override to create own DUChainItemData derived classes DUChainItemData* createData( const DUChainItem& item ) const; //Reimplemented from Base<..> QString itemText( const DUChainItem& data ) const override; KDevelop::IQuickOpen* m_quickopen; private: bool m_openDefinitions; }; #endif diff --git a/plugins/quickopen/projectitemquickopen.cpp b/plugins/quickopen/projectitemquickopen.cpp index 99124d8dee..95ae99cb9b 100644 --- a/plugins/quickopen/projectitemquickopen.cpp +++ b/plugins/quickopen/projectitemquickopen.cpp @@ -1,366 +1,376 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden 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 "projectitemquickopen.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include using namespace KDevelop; namespace { struct SubstringCache { SubstringCache( const QString& string = QString() ) : substring(string) { } inline int containedIn( const Identifier& id ) const { int index = id.index(); QHash::const_iterator it = cache.constFind(index); if(it != cache.constEnd()) { return *it; } const QString idStr = id.identifier().str(); int result = idStr.lastIndexOf(substring, -1, Qt::CaseInsensitive); if (result < 0 && !idStr.isEmpty() && !substring.isEmpty()) { // no match; try abbreviations result = matchesAbbreviation(idStr.midRef(0), substring) ? 0 : -1; } //here we shift the values if the matched string is bigger than the substring, //so closer matches will appear first if (result >= 0) { result = result + (idStr.size() - substring.size()); } cache[index] = result; return result; } QString substring; mutable QHash cache; }; struct ClosestMatchToText { ClosestMatchToText( const QHash& _cache ) : cache(_cache) { } /** @brief Calculates the distance to two pre-filtered match items * * @param a The CodeModelView witch represents the first item to be tested * @param b The CodeModelView witch represents the second item to be tested * * @b */ inline bool operator()( const CodeModelViewItem& a, const CodeModelViewItem& b ) const { const int height_a = cache.value(a.m_id.index(), -1); const int height_b = cache.value(b.m_id.index(), -1); Q_ASSERT(height_a != -1); Q_ASSERT(height_b != -1); if (height_a == height_b) { // stable sorting for equal items based on index // TODO: fast string-based sorting in such cases? return a.m_id.index() < b.m_id.index(); } return height_a < height_b; } private: const QHash& cache; }; +Path findProjectForForPath(const IndexedString& path) +{ + const auto model = ICore::self()->projectController()->projectModel(); + const auto item = model->itemForPath(path); + return item ? item->project()->path() : Path(); +} + } ProjectItemDataProvider::ProjectItemDataProvider( KDevelop::IQuickOpen* quickopen ) : m_quickopen(quickopen) { } void ProjectItemDataProvider::setFilterText( const QString& text ) { m_addedItems.clear(); QStringList search(text.split(QStringLiteral("::"), QString::SkipEmptyParts)); for(int a = 0; a < search.count(); ++a) { if(search[a].endsWith(':')) { //Don't get confused while the :: is being typed search[a] = search[a].left(search[a].length()-1); } } if(!search.isEmpty() && search.back().endsWith('(')) { search.back().chop(1); } if(text.isEmpty() || search.isEmpty()) { m_filteredItems = m_currentItems; return; } KDevVarLengthArray cache; foreach(const QString& searchPart, search) { cache.append(SubstringCache(searchPart)); } if(!text.startsWith(m_currentFilter)) { m_filteredItems = m_currentItems; } m_currentFilter = text; QVector oldFiltered = m_filteredItems; QHash heights; m_filteredItems.clear(); foreach(const CodeModelViewItem& item, oldFiltered) { const QualifiedIdentifier& currentId = item.m_id; int last_pos = currentId.count() - 1; int current_height = 0; int distance = 0; //iter over each search item from last to first //this makes easier to calculate the distance based on where we hit the result or nothing //Iterating from the last item to the first is more efficient, as we want to match the //class/function name, which is the last item on the search fields and on the identifier. for(int b = search.count() - 1; b >= 0; --b) { //iter over each id for the current identifier, from last to first for(; last_pos >= 0; --last_pos, distance++) { // the more distant we are from the class definition, the less priority it will have current_height += distance * 10000; int result; //if the current search item is contained on the current identifier if((result = cache[b].containedIn( currentId.at(last_pos) )) >= 0) { //when we find a hit, whe add the distance to the searched word. //so the closest item will be displayed first current_height += result; if(b == 0) { heights[currentId.index()] = current_height; m_filteredItems << item; } break; } } } } //then, for the last part, we use the already built cache to sort the items according with their distance std::sort(m_filteredItems.begin(), m_filteredItems.end(), ClosestMatchToText(heights)); } QList ProjectItemDataProvider::data( uint start, uint end ) const { QList ret; for(uint a = start; a < end; ++a) { ret << data(a); } return ret; } KDevelop::QuickOpenDataPointer ProjectItemDataProvider::data( uint pos ) const { //Check whether this position falls into an appended item-list, else apply the offset uint filteredItemOffset = 0; for(AddedItems::const_iterator it = m_addedItems.constBegin(); it != m_addedItems.constEnd(); ++it) { int offsetInAppended = pos - (it.key()+1); if(offsetInAppended >= 0 && offsetInAppended < it.value().count()) { return it.value()[offsetInAppended]; } if(it.key() >= pos) { break; } filteredItemOffset += it.value().count(); } const uint a = pos - filteredItemOffset; if(a > (uint)m_filteredItems.size()) { return KDevelop::QuickOpenDataPointer(); } const auto& filteredItem = m_filteredItems[a]; QList ret; KDevelop::DUChainReadLocker lock( DUChain::lock() ); TopDUContext* ctx = DUChainUtils::standardContextForUrl(filteredItem.m_file.toUrl()); if(ctx) { QList decls = ctx->findDeclarations(filteredItem.m_id, CursorInRevision::invalid(), AbstractType::Ptr(), 0, DUContext::DirectQualifiedLookup); //Filter out forward-declarations or duplicate imported declarations foreach(Declaration* decl, decls) { bool filter = false; if (decls.size() > 1 && decl->isForwardDeclaration()) { filter = true; } else if (decl->url() != filteredItem.m_file && m_files.contains(decl->url())) { filter = true; } if (filter) { decls.removeOne(decl); } } foreach(Declaration* decl, decls) { DUChainItem item; item.m_item = decl; item.m_text = decl->qualifiedIdentifier().toString(); + item.m_projectPath = findProjectForForPath(filteredItem.m_file); ret << QuickOpenDataPointer(new DUChainItemData(item)); } if(decls.isEmpty()) { DUChainItem item; item.m_text = filteredItem.m_id.toString(); + item.m_projectPath = findProjectForForPath(filteredItem.m_file); ret << QuickOpenDataPointer(new DUChainItemData(item)); } } else { qCDebug(PLUGIN_QUICKOPEN) << "Could not find standard-context"; } if(!ret.isEmpty()) { QList append = ret.mid(1); if(!append.isEmpty()) { AddedItems addMap; for(AddedItems::iterator it = m_addedItems.begin(); it != m_addedItems.end();) { if(it.key() == pos) { //There already is appended data stored, nothing to do return ret.first(); } else if(it.key() > pos) { addMap[it.key() + append.count()] = it.value(); it = m_addedItems.erase(it); } else { ++it; } } m_addedItems.insert(pos, append); for(AddedItems::const_iterator it = addMap.constBegin(); it != addMap.constEnd(); ++it) { m_addedItems.insert(it.key(), it.value()); } } return ret.first(); } else { return KDevelop::QuickOpenDataPointer(); } } void ProjectItemDataProvider::reset() { m_files = m_quickopen->fileSet(); m_currentItems.clear(); m_addedItems.clear(); KDevelop::DUChainReadLocker lock( DUChain::lock() ); foreach( const IndexedString& u, m_files ) { uint count; const KDevelop::CodeModelItem* items; CodeModel::self().items( u, count, items ); for(uint a = 0; a < count; ++a) { if(!items[a].id.isValid() || items[a].kind & CodeModelItem::ForwardDeclaration) { continue; } if(((m_itemTypes & Classes) && (items[a].kind & CodeModelItem::Class)) || ((m_itemTypes & Functions) && (items[a].kind & CodeModelItem::Function))) { QualifiedIdentifier id = items[a].id.identifier(); if (id.isEmpty() || id.at(0).identifier().isEmpty()) { // id.isEmpty() not always hit when .toString() is actually empty... // anyhow, this makes sure that we don't show duchain items without // any name that could be searched for. This happens e.g. in the c++ // plugin for anonymous structs or sometimes for declarations in macro // expressions continue; } m_currentItems << CodeModelViewItem(u, id); } } } m_filteredItems = m_currentItems; m_currentFilter.clear(); } uint addedItems(const AddedItems& items) { uint add = 0; for(AddedItems::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { add += it.value().count(); } return add; } uint ProjectItemDataProvider::itemCount() const { return m_filteredItems.count() + addedItems(m_addedItems); } uint ProjectItemDataProvider::unfilteredItemCount() const { return m_currentItems.count() + addedItems(m_addedItems); } QStringList ProjectItemDataProvider::supportedItemTypes() { QStringList ret; ret << i18n("Classes"); ret << i18n("Functions"); return ret; } void ProjectItemDataProvider::enableData( const QStringList& items, const QStringList& scopes ) { //FIXME: property support different scopes if(scopes.contains(i18n("Project"))) { m_itemTypes = NoItems; if(items.contains(i18n("Classes"))) { m_itemTypes = (ItemTypes)(m_itemTypes | Classes); } if( items.contains(i18n("Functions"))) { m_itemTypes = (ItemTypes)(m_itemTypes | Functions); } } else { m_itemTypes = NoItems; } } diff --git a/plugins/quickopen/quickopenmodel.cpp b/plugins/quickopen/quickopenmodel.cpp index d9ce2d38ee..0c46d3da47 100644 --- a/plugins/quickopen/quickopenmodel.cpp +++ b/plugins/quickopen/quickopenmodel.cpp @@ -1,447 +1,450 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden 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 "debug.h" #include #include #include #include #include #include "expandingtree/expandingtree.h" #include "projectfilequickopen.h" +#include "duchainitemquickopen.h" #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, &QTimer::timeout, this, &QuickOpenModel::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 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::fromList(scopes); e.types = QSet::fromList(types); e.provider = provider; m_providers << e; //.insert( types, e ); connect( provider, &QuickOpenDataProviderBase::destroyed, this, &QuickOpenModel::destroyed ); 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, &QuickOpenDataProviderBase::destroyed, this, &QuickOpenModel::destroyed ); ret = true; break; } } restart(true); return ret; } void QuickOpenModel::enableProviders( const QStringList& _items, const QStringList& _scopes ) { QSet items = QSet::fromList( _items ); QSet scopes = QSet::fromList( _scopes ); if (m_enabledItems == items && m_enabledScopes == scopes && !items.isEmpty() && !scopes.isEmpty()) { return; } m_enabledItems = items; m_enabledScopes = scopes; qCDebug(PLUGIN_QUICKOPEN) << "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((*it).provider) ) continue; qCDebug(PLUGIN_QUICKOPEN) << "comparing" << (*it).scopes << (*it).types; if( ( scopes.isEmpty() || !( scopes & (*it).scopes ).isEmpty() ) && ( !( items & (*it).types ).isEmpty() || items.isEmpty() ) ) { qCDebug(PLUGIN_QUICKOPEN) << "enabling " << (*it).types << " " << (*it).scopes; (*it).enabled = true; (*it).provider->enableData( _items, _scopes ); } else { qCDebug(PLUGIN_QUICKOPEN) << "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((*it).provider) ) continue; qCDebug(PLUGIN_QUICKOPEN) << "comparing" << (*it).scopes << (*it).types; if( ( scopes.isEmpty() || !( scopes & (*it).scopes ).isEmpty() ) && ( !( items & (*it).types ).isEmpty() || items.isEmpty() ) ) { qCDebug(PLUGIN_QUICKOPEN) << "enabling " << (*it).types << " " << (*it).scopes; (*it).enabled = true; (*it).provider->enableData( _items, _scopes ); } else { qCDebug(PLUGIN_QUICKOPEN) << "disabling " << (*it).types << " " << (*it).scopes; (*it).enabled = false; } } restart(true); } void QuickOpenModel::textChanged( const QString& str ) { if( m_filterText == str ) return; beginResetModel(); 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); endResetModel(); } 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(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(provider.provider) ) continue; if( provider.enabled && provider.provider ) provider.provider->reset(); } if(keepFilterText) { textChanged(m_filterText); }else{ beginResetModel(); m_cachedData.clear(); clearExpanding(); endResetModel(); } } void QuickOpenModel::destroyed( QObject* obj ) { removeProvider( static_cast(obj) ); } QModelIndex QuickOpenModel::index( int row, int column, const QModelIndex& /*parent*/) const { if( column >= columnCount() || row >= rowCount(QModelIndex()) ) return QModelIndex(); if (row < 0 || column < 0) 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(w); return v; } case ExpandingTree::ProjectPathRole: // TODO: put this into the QuickOpenDataBase API // we cannot do this in 5.0, cannot change ABI if (auto projectFile = dynamic_cast(d.constData())) { return QVariant::fromValue(projectFile->projectPath()); + } else if (auto duchainItem = dynamic_cast(d.constData())) { + return QVariant::fromValue(duchainItem->projectPath()); } } 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() { int currentRow = treeView() ? treeView()->currentIndex().row() : -1; beginResetModel(); //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; } endResetModel(); if (currentRow != -1) { treeView()->setCurrentIndex(index(currentRow, 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) { qCDebug(PLUGIN_QUICKOPEN) << "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(); } } // qWarning() << "No item for row " << row; return QuickOpenDataPointer(); } QSet QuickOpenModel::fileSet() const { QSet merged; foreach( const ProviderEntry& provider, m_providers ) { if( m_enabledScopes.isEmpty() || !( m_enabledScopes & provider.scopes ).isEmpty() ) { if( QuickOpenFileSetInterface* iface = dynamic_cast(provider.provider) ) { QSet ifiles = iface->files(); //qCDebug(PLUGIN_QUICKOPEN) << "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 ) { qCDebug(PLUGIN_QUICKOPEN) << "executing model"; if( !index.isValid() ) { qWarning() << "Invalid index executed"; return false; } QuickOpenDataPointer item = getItem( index.row() ); if( item ) { return item->execute( filterText ); }else{ qWarning() << "Got no item for row " << index.row() << " "; } return false; }