diff --git a/plugins/quickopen/expandingtree/expandingtree.cpp b/plugins/quickopen/expandingtree/expandingtree.cpp index 1de5ae2cfb..54682ddc34 100644 --- a/plugins/quickopen/expandingtree/expandingtree.cpp +++ b/plugins/quickopen/expandingtree/expandingtree.cpp @@ -1,63 +1,78 @@ /* This file is part of the KDE libraries and the Kate part. * * 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 as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 "expandingtree.h" #include #include #include #include #include "expandingwidgetmodel.h" +#include +#include + +using namespace KDevelop; + ExpandingTree::ExpandingTree(QWidget* parent) : QTreeView(parent) { m_drawText.documentLayout()->setPaintDevice(this); setUniformRowHeights(false); } void ExpandingTree::drawRow ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { QTreeView::drawRow( painter, option, index ); const ExpandingWidgetModel* eModel = qobject_cast(model()); if( eModel && eModel->isPartiallyExpanded( index ) ) { QRect rect = eModel->partialExpandRect( index ); if( rect.isValid() ) { painter->fillRect(rect,QBrush(0xffffffff)); QAbstractTextDocumentLayout::PaintContext ctx; // since arbitrary HTML can be shown use a black on white color scheme here ctx.palette = QPalette( Qt::black, Qt::white ); ctx.clip = QRectF(0,0,rect.width(),rect.height());; painter->setViewTransformEnabled(true); painter->translate(rect.left(), rect.top()); m_drawText.setHtml( eModel->partialExpandText( index ) ); m_drawText.setPageSize(QSizeF(rect.width(), rect.height())); m_drawText.documentLayout()->draw( painter, ctx ); painter->translate(-rect.left(), -rect.top()); } } } int ExpandingTree::sizeHintForColumn ( int column ) const { return columnWidth( column ); } + +void ExpandingTree::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const +{ + const auto& path = index.data(ProjectPathRole).value(); + if (path.isValid()) { + const auto color = WidgetColorizer::colorForId(qHash(path), palette(), true); + WidgetColorizer::drawBranches(this, painter, rect, index, color); + } + QTreeView::drawBranches(painter, rect, index); +} diff --git a/plugins/quickopen/expandingtree/expandingtree.h b/plugins/quickopen/expandingtree/expandingtree.h index df92444fd0..264be34147 100644 --- a/plugins/quickopen/expandingtree/expandingtree.h +++ b/plugins/quickopen/expandingtree/expandingtree.h @@ -1,39 +1,46 @@ /* This file is part of the KDE libraries and the Kate part. * * 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 as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 KDEVPLATFORM_PLUGIN_EXPANDINGTREE_H #define KDEVPLATFORM_PLUGIN_EXPANDINGTREE_H #include #include //A tree that allows drawing additional information class ExpandingTree : public QTreeView { Q_OBJECT public: - explicit ExpandingTree(QWidget* parent); + explicit ExpandingTree(QWidget* parent); + + enum CustomRoles { + ProjectPathRole = Qt::UserRole + 5000 + }; + protected: virtual void drawRow ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const override; virtual int sizeHintForColumn ( int column ) const override; + + void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; private: mutable QTextDocument m_drawText; }; #endif // KDEVPLATFORM_PLUGIN_EXPANDINGTREE_H diff --git a/plugins/quickopen/projectfilequickopen.cpp b/plugins/quickopen/projectfilequickopen.cpp index e5b7595a69..0679955793 100644 --- a/plugins/quickopen/projectfilequickopen.cpp +++ b/plugins/quickopen/projectfilequickopen.cpp @@ -1,363 +1,368 @@ /* 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 "projectfilequickopen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../openwith/iopenwith.h" using namespace KDevelop; namespace { QSet openFiles() { QSet openFiles; const QList& docs = ICore::self()->documentController()->openDocuments(); openFiles.reserve(docs.size()); foreach( IDocument* doc, docs ) { openFiles << IndexedString(doc->url()); } return openFiles; } QString iconNameForUrl(const IndexedString& url) { if (url.isEmpty()) { return QStringLiteral("tab-duplicate"); } ProjectBaseItem* item = ICore::self()->projectController()->projectModel()->itemForPath(url); if (item) { return item->iconName(); } return QStringLiteral("unknown"); } } ProjectFileData::ProjectFileData( const ProjectFile& file ) : m_file(file) { } QString ProjectFileData::text() const { return m_file.projectPath.relativePath(m_file.path); } QString ProjectFileData::htmlDescription() const { return "" + i18nc("%1: project name", "Project %1", project()) + ""; } bool ProjectFileData::execute( QString& filterText ) { const QUrl url = m_file.path.toUrl(); IOpenWith::openFiles(QList() << url); auto cursor = KTextEditorHelpers::extractCursor(filterText); if (cursor.isValid()) { IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if (doc) { doc->setCursorPosition(cursor); } } return true; } bool ProjectFileData::isExpandable() const { return true; } QList ProjectFileData::highlighting() const { QTextCharFormat boldFormat; boldFormat.setFontWeight(QFont::Bold); QTextCharFormat normalFormat; QString txt = text(); QList ret; int fileNameLength = m_file.path.lastPathSegment().length(); ret << 0; ret << txt.length() - fileNameLength; ret << QVariant(normalFormat); ret << txt.length() - fileNameLength; ret << fileNameLength; ret << QVariant(boldFormat); return ret; } QWidget* ProjectFileData::expandingWidget() const { const QUrl url = m_file.path.toUrl(); DUChainReadLocker lock; ///Find a du-chain for the document QList contexts = DUChain::self()->chainsForDocument(url); ///Pick a non-proxy context TopDUContext* chosen = 0; foreach( TopDUContext* ctx, contexts ) { if( !(ctx->parsingEnvironmentFile() && ctx->parsingEnvironmentFile()->isProxyContext()) ) { chosen = ctx; } } if( chosen ) { return chosen->createNavigationWidget(0, 0, "" + i18nc("%1: project name", "Project %1", project()) + ""); } else { QTextBrowser* ret = new QTextBrowser(); ret->resize(400, 100); ret->setText( "" + i18nc("%1: project name", "Project %1", project()) + "
" + i18n("Not parsed yet") + "
"); return ret; } return 0; } QIcon ProjectFileData::icon() const { const QString& iconName = iconNameForUrl(m_file.indexedPath); /** * FIXME: Move this cache into a more central place and reuse it elsewhere. * The project model e.g. could reuse this as well. * * Note: We cache here since otherwise displaying and esp. scrolling * in a large list of quickopen items becomes very slow. */ static QHash iconCache; QHash< QString, QPixmap >::const_iterator it = iconCache.constFind(iconName); if (it != iconCache.constEnd()) { return it.value(); } const QPixmap& pixmap = KIconLoader::global()->loadIcon(iconName, KIconLoader::Small); iconCache.insert(iconName, pixmap); return pixmap; } QString ProjectFileData::project() const { const IProject* project = ICore::self()->projectController()->findProjectForUrl(m_file.path.toUrl()); if (project) { return project->name(); } else { return i18n("none"); } } +Path ProjectFileData::projectPath() const +{ + return m_file.projectPath; +} + BaseFileDataProvider::BaseFileDataProvider() { } void BaseFileDataProvider::setFilterText( const QString& text ) { int pathLength; KTextEditorHelpers::extractCursor(text, &pathLength); QString path(text.mid(0, pathLength)); if ( path.startsWith(QLatin1String("./")) || path.startsWith(QLatin1String("../")) ) { // assume we want to filter relative to active document's url IDocument* doc = ICore::self()->documentController()->activeDocument(); if (doc) { path = Path(Path(doc->url()).parent(), path).pathOrUrl(); } } setFilter( path.split('/', QString::SkipEmptyParts) ); } uint BaseFileDataProvider::itemCount() const { return filteredItems().count(); } uint BaseFileDataProvider::unfilteredItemCount() const { return items().count(); } QuickOpenDataPointer BaseFileDataProvider::data(uint row) const { return QuickOpenDataPointer(new ProjectFileData( filteredItems().at(row) )); } ProjectFileDataProvider::ProjectFileDataProvider() { auto projectController = ICore::self()->projectController(); connect(projectController, &IProjectController::projectClosing, this, &ProjectFileDataProvider::projectClosing); connect(projectController, &IProjectController::projectOpened, this, &ProjectFileDataProvider::projectOpened); foreach (const auto project, projectController->projects()) { projectOpened(project); } } void ProjectFileDataProvider::projectClosing( IProject* project ) { foreach(ProjectFileItem* file, KDevelop::allFiles(project->projectItem())) { fileRemovedFromSet(file); } } void ProjectFileDataProvider::projectOpened( IProject* project ) { const int processAfter = 1000; int processed = 0; foreach(ProjectFileItem* file, KDevelop::allFiles(project->projectItem())) { fileAddedToSet(file); if (++processed == processAfter) { // prevent UI-lockup when a huge project was imported QApplication::processEvents(); processed = 0; } } connect(project, &IProject::fileAddedToSet, this, &ProjectFileDataProvider::fileAddedToSet); connect(project, &IProject::fileRemovedFromSet, this, &ProjectFileDataProvider::fileRemovedFromSet); } void ProjectFileDataProvider::fileAddedToSet( ProjectFileItem* file ) { ProjectFile f; f.projectPath = file->project()->path(); f.path = file->path(); f.indexedPath = file->indexedPath(); f.outsideOfProject = !f.projectPath.isParentOf(f.path); auto it = std::lower_bound(m_projectFiles.begin(), m_projectFiles.end(), f); if (it == m_projectFiles.end() || it->path != f.path) { m_projectFiles.insert(it, f); } } void ProjectFileDataProvider::fileRemovedFromSet( ProjectFileItem* file ) { ProjectFile item; item.path = file->path(); // fast-path for non-generated files // NOTE: figuring out whether something is generated is expensive... and since // generated files are rare we apply this two-step algorithm here auto it = std::lower_bound(m_projectFiles.begin(), m_projectFiles.end(), item); if (it != m_projectFiles.end() && !(item < *it)) { m_projectFiles.erase(it); return; } // last try: maybe it was generated item.outsideOfProject = true; it = std::lower_bound(m_projectFiles.begin(), m_projectFiles.end(), item); if (it != m_projectFiles.end() && !(item < *it)) { m_projectFiles.erase(it); return; } } void ProjectFileDataProvider::reset() { clearFilter(); QList projectFiles = m_projectFiles; const auto& open = openFiles(); for(QList::iterator it = projectFiles.begin(); it != projectFiles.end();) { if (open.contains(it->indexedPath)) { it = projectFiles.erase(it); } else { ++it; } } setItems(projectFiles); } QSet ProjectFileDataProvider::files() const { QSet ret; foreach( IProject* project, ICore::self()->projectController()->projects() ) ret += project->fileSet(); return ret - openFiles(); } void OpenFilesDataProvider::reset() { clearFilter(); IProjectController* projCtrl = ICore::self()->projectController(); IDocumentController* docCtrl = ICore::self()->documentController(); const QList& docs = docCtrl->openDocuments(); QList currentFiles; currentFiles.reserve(docs.size()); foreach( IDocument* doc, docs ) { ProjectFile f; f.path = Path(doc->url()); IProject* project = projCtrl->findProjectForUrl(doc->url()); if (project) { f.projectPath = project->path(); } currentFiles << f; } std::sort(currentFiles.begin(), currentFiles.end()); setItems(currentFiles); } QSet OpenFilesDataProvider::files() const { return openFiles(); } diff --git a/plugins/quickopen/projectfilequickopen.h b/plugins/quickopen/projectfilequickopen.h index 097fc7ac0b..469f2a5ed4 100644 --- a/plugins/quickopen/projectfilequickopen.h +++ b/plugins/quickopen/projectfilequickopen.h @@ -1,144 +1,146 @@ /* 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 PROJECT_FILE_QUICKOPEN #define PROJECT_FILE_QUICKOPEN #include #include #include #include namespace KDevelop { class IProject; class ProjectFileItem; } class QIcon; /** * Internal data class for the BaseFileDataProvider and ProjectFileData. */ struct ProjectFile { ProjectFile() : outsideOfProject(false) {} KDevelop::Path path; // project root folder url KDevelop::Path projectPath; // indexed url - only set for project files // currently open documents don't use this! KDevelop::IndexedString indexedPath; // true for files which reside outside of the project root // this happens e.g. for generated files in out-of-source build folders bool outsideOfProject; }; inline bool operator<(const ProjectFile& left, const ProjectFile& right) { if (left.outsideOfProject != right.outsideOfProject) { return !left.outsideOfProject; } return left.path < right.path; } Q_DECLARE_TYPEINFO(ProjectFile, Q_MOVABLE_TYPE); /** * The shared data class that is used by the quick open model. */ class ProjectFileData : public KDevelop::QuickOpenDataBase { public: explicit ProjectFileData( const ProjectFile& file ); QString text() const override; QString htmlDescription() const override; bool execute( QString& filterText ) override; bool isExpandable() const override; QWidget* expandingWidget() const override; QIcon icon() const override; QList highlighting() const override; QString project() const; + KDevelop::Path projectPath() const; + private: ProjectFile m_file; }; class BaseFileDataProvider : public KDevelop::QuickOpenDataProviderBase, public KDevelop::PathFilter, public KDevelop::QuickOpenFileSetInterface { Q_OBJECT public: BaseFileDataProvider(); void setFilterText( const QString& text ) override; uint itemCount() const override; uint unfilteredItemCount() const override; KDevelop::QuickOpenDataPointer data( uint row ) const override; inline KDevelop::Path itemPath( const ProjectFile& data ) const { return data.path; } }; /** * QuickOpen data provider for file-completion using project-files. * * It provides all files from all open projects except currently opened ones. */ class ProjectFileDataProvider : public BaseFileDataProvider { Q_OBJECT public: ProjectFileDataProvider(); void reset() override; QSet files() const override; private slots: void projectClosing( KDevelop::IProject* ); void projectOpened( KDevelop::IProject* ); void fileAddedToSet( KDevelop::ProjectFileItem* ); void fileRemovedFromSet( KDevelop::ProjectFileItem* ); private: // project files sorted by their url // this is done so we can limit ourselves to a relatively fast // filtering without any expensive sorting in reset(). QList m_projectFiles; }; /** * Quick open data provider for currently opened documents. */ class OpenFilesDataProvider : public BaseFileDataProvider { Q_OBJECT public: void reset() override; QSet files() const override; }; #endif diff --git a/plugins/quickopen/quickopenmodel.cpp b/plugins/quickopen/quickopenmodel.cpp index 59419d2f03..d9ce2d38ee 100644 --- a/plugins/quickopen/quickopenmodel.cpp +++ b/plugins/quickopen/quickopenmodel.cpp @@ -1,438 +1,447 @@ /* 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" + #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()); + } } 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; }