diff --git a/plugins/documentview/CMakeLists.txt b/plugins/documentview/CMakeLists.txt index aba4089a77..f8aba7445d 100644 --- a/plugins/documentview/CMakeLists.txt +++ b/plugins/documentview/CMakeLists.txt @@ -1,21 +1,22 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevdocumentview\") project(documentview) #add_subdirectory(settings) ########### next target ############### set(kdevdocumentview_PART_SRCS kdevdocumentview.cpp kdevdocumentviewdelegate.cpp kdevdocumentviewplugin.cpp kdevdocumentmodel.cpp kdevdocumentselection.cpp ) qt5_add_resources(kdevdocumentview_PART_SRCS kdevdocumentview.qrc) kdevplatform_add_plugin(kdevdocumentview JSON kdevdocumentview.json SOURCES ${kdevdocumentview_PART_SRCS}) target_link_libraries(kdevdocumentview KDev::Interfaces + KDev::Util KF5::TextEditor ) diff --git a/plugins/documentview/kdevdocumentmodel.cpp b/plugins/documentview/kdevdocumentmodel.cpp index 5bbc1f6803..ab75a35ac7 100644 --- a/plugins/documentview/kdevdocumentmodel.cpp +++ b/plugins/documentview/kdevdocumentmodel.cpp @@ -1,158 +1,166 @@ /* This file is part of KDevelop Copyright 2005 Adam Treat Copyright 2013 Sebastian Kügler 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 "kdevdocumentmodel.h" #include using namespace KDevelop; KDevDocumentItem::KDevDocumentItem( const QString &name ) : QStandardItem(name) , m_documentState(IDocument::Clean) { setIcon( icon() ); } KDevDocumentItem::~KDevDocumentItem() {} QIcon KDevDocumentItem::icon() const { switch(m_documentState) { case IDocument::Clean: return QIcon::fromTheme(m_fileIcon); case IDocument::Modified: return QIcon::fromTheme(QStringLiteral("document-save")); case IDocument::Dirty: return QIcon::fromTheme(QStringLiteral("document-revert")); case IDocument::DirtyAndModified: return QIcon::fromTheme(QStringLiteral("edit-delete")); default: return QIcon(); } } IDocument::DocumentState KDevDocumentItem::documentState() const { return m_documentState; } void KDevDocumentItem::setDocumentState(IDocument::DocumentState state) { m_documentState = state; setIcon(icon()); } const QUrl KDevDocumentItem::url() const { return m_url; } void KDevDocumentItem::setUrl(const QUrl& url) { m_url = url; } +QVariant KDevDocumentItem::data(int role) const +{ + if (role == UrlRole) { + return m_url; + } + return QStandardItem::data(role); +} + KDevCategoryItem::KDevCategoryItem( const QString &name ) : KDevDocumentItem( name ) { setFlags(Qt::ItemIsEnabled); setToolTip( name ); setIcon( QIcon::fromTheme( QStringLiteral( "folder") ) ); } KDevCategoryItem::~KDevCategoryItem() {} QList KDevCategoryItem::fileList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { if ( KDevFileItem * item = dynamic_cast( child( i ) ) ->fileItem() ) lst.append( item ); } return lst; } KDevFileItem* KDevCategoryItem::file( const QUrl &url ) const { foreach( KDevFileItem * item, fileList() ) { if ( item->url() == url ) return item; } return 0; } KDevFileItem::KDevFileItem( const QUrl &url ) : KDevDocumentItem( url.fileName() ) { setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); setUrl( url ); if (!url.isEmpty()) { m_fileIcon = KFileItem(url, QString(), 0).iconName(); } setIcon( QIcon::fromTheme( m_fileIcon ) ); } KDevFileItem::~KDevFileItem() {} KDevDocumentModel::KDevDocumentModel( QObject *parent ) : QStandardItemModel( parent ) { setRowCount(0); setColumnCount(1); } KDevDocumentModel::~KDevDocumentModel() {} QList KDevDocumentModel::categoryList() const { QList lst; for ( int i = 0; i < rowCount() ; ++i ) { if ( KDevCategoryItem * categoryitem = dynamic_cast( item( i ) ) ->categoryItem() ) { lst.append( categoryitem ); } } return lst; } KDevCategoryItem* KDevDocumentModel::category( const QString& category ) const { foreach( KDevCategoryItem * item, categoryList() ) { if ( item->toolTip() == category ) return item; } return 0; } diff --git a/plugins/documentview/kdevdocumentmodel.h b/plugins/documentview/kdevdocumentmodel.h index 0c88bc07de..2229562ce3 100644 --- a/plugins/documentview/kdevdocumentmodel.h +++ b/plugins/documentview/kdevdocumentmodel.h @@ -1,106 +1,112 @@ /* This file is part of KDevelop Copyright 2005 Adam Treat Copyright 2013 Sebastian Kügler 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_KDEVDOCUMENTMODEL_H #define KDEVPLATFORM_PLUGIN_KDEVDOCUMENTMODEL_H #include #include #include #include #include class KDevDocumentItem; class KDevCategoryItem; class KDevFileItem; class KDevDocumentItem: public QStandardItem { public: explicit KDevDocumentItem( const QString &name ); ~KDevDocumentItem() override; virtual KDevCategoryItem *categoryItem() const { return nullptr; } virtual KDevFileItem *fileItem() const { return nullptr; } QIcon icon() const; KDevelop::IDocument::DocumentState documentState() const; void setDocumentState( KDevelop::IDocument::DocumentState state ); const QUrl url() const; void setUrl(const QUrl &url); + QVariant data(int role) const override; + + enum Roles { + UrlRole = Qt::UserRole + 1 + }; + protected: QString m_fileIcon; private: QUrl m_url; KDevelop::IDocument::DocumentState m_documentState; }; class KDevCategoryItem: public KDevDocumentItem { public: explicit KDevCategoryItem( const QString &name ); ~KDevCategoryItem() override; KDevCategoryItem *categoryItem() const override { return const_cast( this ); } QList fileList() const; KDevFileItem* file( const QUrl &url ) const; }; class KDevFileItem: public KDevDocumentItem { public: explicit KDevFileItem( const QUrl &url ); ~KDevFileItem() override; KDevFileItem *fileItem() const override { return const_cast( this ); } }; class KDevDocumentModel: public QStandardItemModel { Q_OBJECT public: explicit KDevDocumentModel( QObject *parent = nullptr ); ~KDevDocumentModel() override; QList categoryList() const; KDevCategoryItem* category( const QString& category ) const; }; #endif // KDEVPLATFORM_PLUGIN_KDEVDOCUMENTMODEL_H diff --git a/plugins/documentview/kdevdocumentview.cpp b/plugins/documentview/kdevdocumentview.cpp index a810a0033b..97601f04a4 100644 --- a/plugins/documentview/kdevdocumentview.cpp +++ b/plugins/documentview/kdevdocumentview.cpp @@ -1,349 +1,369 @@ /* This file is part of KDevelop Copyright 2005 Adam Treat Copyright 2013 Sebastian Kügler 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 "kdevdocumentview.h" #include "kdevdocumentviewplugin.h" #include "kdevdocumentmodel.h" #include #include #include #include #include #include #include #include +#include +#include #include "kdevdocumentselection.h" #include "kdevdocumentviewdelegate.h" #include #include #include #include #include #include #include #include +#include +#include + using namespace KDevelop; KDevDocumentView::KDevDocumentView( KDevDocumentViewPlugin *plugin, QWidget *parent ) : QTreeView( parent ), m_plugin( plugin ) { connect(ICore::self()->projectController(), &IProjectController::projectOpened, this, &KDevDocumentView::updateProjectPaths); connect(ICore::self()->projectController(), &IProjectController::projectClosed, this, &KDevDocumentView::updateProjectPaths); m_documentModel = new KDevDocumentModel(this); m_delegate = new KDevDocumentViewDelegate( this ); m_proxy = new QSortFilterProxyModel( this ); m_proxy->setSourceModel( m_documentModel ); m_proxy->setDynamicSortFilter( true ); m_proxy->setSortCaseSensitivity( Qt::CaseInsensitive ); m_proxy->sort(0); m_selectionModel = new KDevDocumentSelection( m_proxy ); setModel( m_proxy ); setSelectionModel( m_selectionModel ); setItemDelegate( m_delegate ); setObjectName( i18n( "Documents" ) ); setWindowIcon( QIcon::fromTheme( QStringLiteral( "document-multiple" ), windowIcon() ) ); setWindowTitle( i18n( "Documents" ) ); setFocusPolicy( Qt::NoFocus ); header()->hide(); setSelectionBehavior( QAbstractItemView::SelectRows ); setSelectionMode( QAbstractItemView::ExtendedSelection ); updateProjectPaths(); } KDevDocumentView::~KDevDocumentView() {} KDevDocumentViewPlugin *KDevDocumentView::plugin() const { return m_plugin; } void KDevDocumentView::mousePressEvent( QMouseEvent * event ) { QModelIndex proxyIndex = indexAt( event->pos() ); QModelIndex index = m_proxy->mapToSource( proxyIndex ); if (event->button() == Qt::LeftButton && event->modifiers() == Qt::NoModifier) { if (proxyIndex.parent().isValid()) { // this is a document item KDevelop::IDocumentController* dc = m_plugin->core()->documentController(); QUrl documentUrl = static_cast(m_documentModel->itemFromIndex(index))->fileItem()->url(); if (dc->documentForUrl(documentUrl) != dc->activeDocument()) { dc->openDocument(documentUrl); return; } } else { // this is a folder item setExpanded(proxyIndex, !isExpanded(proxyIndex)); return; } } QTreeView::mousePressEvent( event ); } template void KDevDocumentView::visitItems(F f, bool selectedItems) { KDevelop::IDocumentController* dc = m_plugin->core()->documentController(); QList docs = selectedItems ? m_selectedDocs : m_unselectedDocs; foreach(const QUrl& url, docs) { KDevelop::IDocument* doc = dc->documentForUrl(url); if (doc) f(doc); } } namespace { class DocSaver { public: void operator()(KDevelop::IDocument* doc) { doc->save(); } }; class DocCloser { public: void operator()(KDevelop::IDocument* doc) { doc->close(); } }; class DocReloader { public: void operator()(KDevelop::IDocument* doc) { doc->reload(); } }; } void KDevDocumentView::saveSelected() { visitItems(DocSaver(), true); } void KDevDocumentView::closeSelected() { visitItems(DocCloser(), true); } void KDevDocumentView::closeUnselected() { visitItems(DocCloser(), false); } void KDevDocumentView::reloadSelected() { visitItems(DocReloader(), true); } void KDevDocumentView::contextMenuEvent( QContextMenuEvent * event ) { QModelIndex proxyIndex = indexAt( event->pos() ); // for now, ignore clicks on empty space or folder items if (!proxyIndex.isValid() || !proxyIndex.parent().isValid()) { return; } updateSelectedDocs(); if (!m_selectedDocs.isEmpty()) { QMenu* ctxMenu = new QMenu(this); KDevelop::FileContext context(m_selectedDocs); QList extensions = m_plugin->core()->pluginController()->queryPluginsForContextMenuExtensions( &context ); QList vcsActions; QList fileActions; QList editActions; QList extensionActions; foreach( const KDevelop::ContextMenuExtension& ext, extensions ) { fileActions += ext.actions(KDevelop::ContextMenuExtension::FileGroup); vcsActions += ext.actions(KDevelop::ContextMenuExtension::VcsGroup); editActions += ext.actions(KDevelop::ContextMenuExtension::EditGroup); extensionActions += ext.actions(KDevelop::ContextMenuExtension::ExtensionGroup); } appendActions(ctxMenu, fileActions); QAction* save = KStandardAction::save(this, SLOT(saveSelected()), ctxMenu); save->setEnabled(selectedDocHasChanges()); ctxMenu->addAction(save); ctxMenu->addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n( "Reload" ), this, SLOT(reloadSelected())); appendActions(ctxMenu, editActions); ctxMenu->addAction(KStandardAction::close(this, SLOT(closeSelected()), ctxMenu)); QAction* closeUnselected = ctxMenu->addAction(QIcon::fromTheme(QStringLiteral("document-close")), i18n( "Close Other Files" ), this, SLOT(closeUnselected())); closeUnselected->setEnabled(!m_unselectedDocs.isEmpty()); appendActions(ctxMenu, vcsActions); appendActions(ctxMenu, extensionActions); connect(ctxMenu, &QMenu::aboutToHide, ctxMenu, &QMenu::deleteLater); ctxMenu->popup( event->globalPos() ); } } void KDevDocumentView::appendActions(QMenu* menu, const QList& actions) { foreach( QAction* act, actions ) { menu->addAction(act); } menu->addSeparator(); } bool KDevDocumentView::selectedDocHasChanges() { KDevelop::IDocumentController* dc = m_plugin->core()->documentController(); foreach(const QUrl& url, m_selectedDocs) { KDevelop::IDocument* doc = dc->documentForUrl(url); if (!doc) continue; if (doc->state() != KDevelop::IDocument::Clean) { return true; } } return false; } void KDevDocumentView::updateSelectedDocs() { m_selectedDocs.clear(); m_unselectedDocs.clear(); QList allItems = m_documentModel->findItems(QStringLiteral("*"), Qt::MatchWildcard | Qt::MatchRecursive); foreach (QStandardItem* item, allItems) { if (KDevFileItem * fileItem = dynamic_cast(item)->fileItem()) { if (m_selectionModel->isSelected(m_proxy->mapFromSource(m_documentModel->indexFromItem(fileItem)))) m_selectedDocs << fileItem->url(); else m_unselectedDocs << fileItem->url(); } } } void KDevDocumentView::activated( KDevelop::IDocument* document ) { setCurrentIndex( m_proxy->mapFromSource( m_documentModel->indexFromItem( m_doc2index[ document ] ) ) ); } void KDevDocumentView::saved( KDevelop::IDocument* ) { } void KDevDocumentView::opened( KDevelop::IDocument* document ) { const QString path = QFileInfo( document->url().path() ).path(); KDevCategoryItem *categoryItem = m_documentModel->category( path ); if ( !categoryItem ) { categoryItem = new KDevCategoryItem( path ); categoryItem->setUrl( document->url() ); m_documentModel->insertRow( m_documentModel->rowCount(), categoryItem ); setExpanded( m_proxy->mapFromSource( m_documentModel->indexFromItem( categoryItem ) ), false); updateCategoryItem( categoryItem ); } if ( !categoryItem->file( document->url() ) ) { KDevFileItem * fileItem = new KDevFileItem( document->url() ); categoryItem->setChild( categoryItem->rowCount(), fileItem ); setCurrentIndex( m_proxy->mapFromSource( m_documentModel->indexFromItem( fileItem ) ) ); m_doc2index[ document ] = fileItem; } } void KDevDocumentView::closed( KDevelop::IDocument* document ) { KDevFileItem* file = m_doc2index[ document ]; if ( !file ) return; QStandardItem* categoryItem = file->parent(); qDeleteAll(categoryItem->takeRow(m_documentModel->indexFromItem(file).row())); m_doc2index.remove(document); if ( categoryItem->hasChildren() ) return; qDeleteAll(m_documentModel->takeRow(m_documentModel->indexFromItem(categoryItem).row())); doItemsLayout(); } void KDevDocumentView::updateCategoryItem( KDevCategoryItem *item ) { QString text = KDevelop::ICore::self()->projectController()->prettyFilePath(item->url(), KDevelop::IProjectController::FormatPlain); // remove trailing slash if (text.length() > 1) { text.chop(1); } item->setText(text); } void KDevDocumentView::updateProjectPaths() { foreach ( KDevCategoryItem *it, m_documentModel->categoryList() ) updateCategoryItem( it ); } void KDevDocumentView::contentChanged( KDevelop::IDocument* ) { } void KDevDocumentView::stateChanged( KDevelop::IDocument* document ) { KDevDocumentItem * documentItem = m_doc2index[ document ]; if ( documentItem && documentItem->documentState() != document->state() ) documentItem->setDocumentState( document->state() ); doItemsLayout(); } void KDevDocumentView::documentUrlChanged( KDevelop::IDocument* document ) { closed(document); opened(document); } +void KDevDocumentView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const +{ + const bool colorizeByProject = KSharedConfig::openConfig()->group("UiSettings").readEntry("ColorizeByProject", false); + if (colorizeByProject) { + const auto url = index.data(KDevDocumentItem::UrlRole).value(); + const auto project = ICore::self()->projectController()->findProjectForUrl(url); + if (project) { + const auto projectPath = project->path(); + const QColor color = WidgetColorizer::colorForId(qHash(projectPath), palette()); + WidgetColorizer::drawBranches(this, painter, rect, index, color); + } + } + + QTreeView::drawBranches(painter, rect, index); +} diff --git a/plugins/documentview/kdevdocumentview.h b/plugins/documentview/kdevdocumentview.h index da1ad8659b..79e353d2e3 100644 --- a/plugins/documentview/kdevdocumentview.h +++ b/plugins/documentview/kdevdocumentview.h @@ -1,98 +1,100 @@ /* This file is part of KDevelop Copyright 2005 Adam Treat Copyright 2013 Sebastian Kügler 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_KDEVDOCUMENTVIEW_H #define KDEVPLATFORM_PLUGIN_KDEVDOCUMENTVIEW_H #include #include class QSortFilterProxyModel; class QAction; class KDevDocumentViewPlugin; class KDevDocumentModel; class KDevDocumentViewDelegate; class KDevDocumentSelection; class KDevFileItem; namespace KDevelop { class IDocument; class IProject; } class KDevCategoryItem; class KDevDocumentModel; class KDevDocumentItem; class KDevDocumentView: public QTreeView { Q_OBJECT public: explicit KDevDocumentView( KDevDocumentViewPlugin *plugin, QWidget *parent ); ~KDevDocumentView() override; KDevDocumentViewPlugin *plugin() const; signals: void activateURL( const QUrl &url ); public slots: void opened( KDevelop::IDocument* document ); private slots: void activated( KDevelop::IDocument* document ); void saved( KDevelop::IDocument* document ); void closed( KDevelop::IDocument* document ); void contentChanged( KDevelop::IDocument* document ); void stateChanged( KDevelop::IDocument* document ); void documentUrlChanged( KDevelop::IDocument* document ); void updateCategoryItem( KDevCategoryItem *item ); void updateProjectPaths(); void saveSelected(); void reloadSelected(); void closeSelected(); void closeUnselected(); protected: void mousePressEvent( QMouseEvent * event ) override; void contextMenuEvent( QContextMenuEvent * event ) override; + void drawBranches(QPainter* painter, const QRect& rect, + const QModelIndex& index) const override; private: template void visitItems(F, bool selectedItems); bool selectedDocHasChanges(); void updateSelectedDocs(); void appendActions(QMenu* menu, const QList< QAction* >& actions); private: KDevDocumentViewPlugin *m_plugin; KDevDocumentModel *m_documentModel; KDevDocumentSelection* m_selectionModel; QSortFilterProxyModel* m_proxy; KDevDocumentViewDelegate* m_delegate; QHash< KDevelop::IDocument*, KDevFileItem* > m_doc2index; QList m_selectedDocs; // used for ctx menu QList m_unselectedDocs; // used for ctx menu friend class KDevDocumentViewPluginFactory; // to connect to the private slots stateChanged and documentUrlChanged }; #endif // KDEVPLATFORM_PLUGIN_KDEVDOCUMENTVIEW_H