/* 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 "../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().pathOrUrl()); } return openFiles; } QString iconNameForUrl(const IndexedString& url) { if (url.isEmpty()) { return QString("tab-duplicate"); } ProjectBaseItem* item = ICore::self()->projectController()->projectModel()->itemForPath(url); if (item) { return item->iconName(); } return QString("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 KUrl url = m_file.path.toUrl(); IOpenWith::openFiles(KUrl::List() << url); QString path; uint lineNumber; if (extractLineNumber(filterText, path, lineNumber)) { IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if (doc) { doc->setCursorPosition(KTextEditor::Cursor(lineNumber - 1, 0)); } } 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 KUrl 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"); } } BaseFileDataProvider::BaseFileDataProvider() { } void BaseFileDataProvider::setFilterText( const QString& text ) { QString path(text); uint lineNumber; extractLineNumber(text, path, lineNumber); 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) { KUrl url = doc->url().upUrl(); url.addPath( path); url.cleanPath(); url.adjustPath(KUrl::RemoveTrailingSlash); path = url.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() { connect(ICore::self()->projectController(), SIGNAL(projectClosing(KDevelop::IProject*)), this, SLOT(projectClosing(KDevelop::IProject*))); connect(ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)), this, SLOT(projectOpened(KDevelop::IProject*))); } void ProjectFileDataProvider::projectClosing( IProject* project ) { foreach(ProjectFileItem* file, project->files()) { fileRemovedFromSet(file); } } void ProjectFileDataProvider::projectOpened( IProject* project ) { const int processAfter = 1000; int processed = 0; foreach(ProjectFileItem* file, project->files()) { fileAddedToSet(file); if (++processed == processAfter) { // prevent UI-lockup when a huge project was imported QApplication::processEvents(); processed = 0; } } connect(project, SIGNAL(fileAddedToSet(KDevelop::ProjectFileItem*)), this, SLOT(fileAddedToSet(KDevelop::ProjectFileItem*))); connect(project, SIGNAL(fileRemovedFromSet(KDevelop::ProjectFileItem*)), this, SLOT(fileRemovedFromSet(KDevelop::ProjectFileItem*))); } 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); QList::iterator it = qLowerBound(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 QList::iterator it = qBinaryFind(m_projectFiles.begin(), m_projectFiles.end(), item); if (it != m_projectFiles.end()) { m_projectFiles.erase(it); return; } // last try: maybe it was generated item.outsideOfProject = true; it = qBinaryFind(m_projectFiles.begin(), m_projectFiles.end(), item); if (it != m_projectFiles.end()) { 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; } qSort(currentFiles); setItems(currentFiles); } QSet OpenFilesDataProvider::files() const { return openFiles(); } #include "moc_projectfilequickopen.cpp"