diff --git a/plugins/documentswitcher/CMakeLists.txt b/plugins/documentswitcher/CMakeLists.txt --- a/plugins/documentswitcher/CMakeLists.txt +++ b/plugins/documentswitcher/CMakeLists.txt @@ -3,6 +3,7 @@ ########### next target ############### set(kdevdocumentswitcher_PART_SRCS + documentswitcheritem.cpp documentswitcherplugin.cpp documentswitchertreeview.cpp ) diff --git a/plugins/documentswitcher/documentswitcheritem.h b/plugins/documentswitcher/documentswitcheritem.h new file mode 100644 --- /dev/null +++ b/plugins/documentswitcher/documentswitcheritem.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright 2009 Andreas Pakulat * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef KDEVPLATFORM_PLUGIN_DOCUMENTSWITCHERITEM_H +#define KDEVPLATFORM_PLUGIN_DOCUMENTSWITCHERITEM_H + +#include + +namespace KDevelop +{ + class IDocument; +} + +/** Helper for list model items. + * Extracts icon and text from document. + */ +class DocumentSwitcherItem : public QStandardItem +{ +public: + explicit DocumentSwitcherItem(KDevelop::IDocument *document); +}; + +#endif // KDEVPLATFORM_PLUGIN_DOCUMENTSWITCHERITEM_H diff --git a/plugins/documentswitcher/documentswitcheritem.cpp b/plugins/documentswitcher/documentswitcheritem.cpp new file mode 100644 --- /dev/null +++ b/plugins/documentswitcher/documentswitcheritem.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright 2009 Andreas Pakulat * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "documentswitcheritem.h" +#include "documentswitchertreeview.h" + +#include + +#include + +#include +#include +#include +#include + +DocumentSwitcherItem::DocumentSwitcherItem(KDevelop::IDocument *document) +{ + const QUrl &url = document->url(); + + KDevelop::IProjectController *projectController = KDevelop::ICore::self()->projectController(); + + // Find file icon : file type icon or document unsaved icon. + switch (document->state()) + { + case KDevelop::IDocument::Clean: + { + setIcon(QIcon::fromTheme(KFileItem(url, QString(), 0).iconName())); + break; + } + case KDevelop::IDocument::Modified: + { + setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); + break; + } + case KDevelop::IDocument::Dirty: + { + setIcon(QIcon::fromTheme(QStringLiteral("document-revert"))); + break; + } + case KDevelop::IDocument::DirtyAndModified: + { + setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); + break; + } + } + + // Extract file name and path. + QString text = url.fileName(); + QString path = projectController->prettyFilePath(url, KDevelop::IProjectController::FormatPlain); + + const bool isPartOfOpenProject = QDir::isRelativePath(path); + if (path.endsWith(QLatin1Char('/'))) { + path.chop(1); + } + if (isPartOfOpenProject) { + const int projectNameSize = path.indexOf(QLatin1Char(':')); + + // first: project name, second: path to file in project (might be just '/' when the file is in the project root dir) + const QPair fileInProjectInfo = (projectNameSize < 0) + ? qMakePair(path, QStringLiteral("/")) + : qMakePair(path.left(projectNameSize), path.mid(projectNameSize + 1)); + + text = QStringLiteral("%1 (%2:%3)").arg(text, fileInProjectInfo.first, fileInProjectInfo.second); + } + else { + text += QLatin1String(" (") + path + QLatin1Char(')'); + } + + setText(text); + + // Set item data. + KDevelop::IProject *project = projectController->findProjectForUrl(url); + setData(QVariant::fromValue(project), DocumentSwitcherTreeView::ProjectRole); +} diff --git a/plugins/documentswitcher/documentswitcherplugin.h b/plugins/documentswitcher/documentswitcherplugin.h --- a/plugins/documentswitcher/documentswitcherplugin.h +++ b/plugins/documentswitcher/documentswitcherplugin.h @@ -22,18 +22,20 @@ #include #include -class QStandardItemModel; namespace Sublime { - class View; class MainWindow; - class Area; -class MainWindow; +} + +namespace KDevelop +{ + class IDocument; } class DocumentSwitcherTreeView; -class QModelIndex; +class QStandardItemModel; +class QModelIndex; class QAction; class DocumentSwitcherPlugin: public KDevelop::IPlugin { @@ -46,26 +48,21 @@ public Q_SLOTS: void itemActivated( const QModelIndex& ); void switchToClicked(const QModelIndex& ); -private Q_SLOTS: - void addView( Sublime::View* ); - void changeView( Sublime::View* ); - void addMainWindow( Sublime::MainWindow* ); - void changeArea( Sublime::Area* ); - void removeView( Sublime::View* ); - void removeMainWindow(QObject*); void walkForward(); void walkBackward(); + void documentOpened(KDevelop::IDocument *document); + void documentActivated(KDevelop::IDocument *document); + void documentClosed(KDevelop::IDocument *document); protected: bool eventFilter( QObject*, QEvent* ) override; private: void setViewGeometry(Sublime::MainWindow* window); - void storeAreaViewList( Sublime::MainWindow* mainwindow, Sublime::Area* area ); void enableActions(); - void fillModel( Sublime::MainWindow* window ); + void fillModel(); void walk(const int from, const int to); - // Need to use QObject here as we only have a QObject* in - // the removeMainWindow method and cannot cast it to the mainwindow anymore - QMap > > documentLists; + + // List of opened document sorted activation. + QList documentLists; DocumentSwitcherTreeView* view; QStandardItemModel* model; QAction* forwardAction; diff --git a/plugins/documentswitcher/documentswitcherplugin.cpp b/plugins/documentswitcher/documentswitcherplugin.cpp --- a/plugins/documentswitcher/documentswitcherplugin.cpp +++ b/plugins/documentswitcher/documentswitcherplugin.cpp @@ -20,7 +20,6 @@ #include "documentswitcherplugin.h" #include -#include #include #include #include @@ -31,17 +30,13 @@ #include #include -#include -#include -#include +#include +#include #include -#include #include "documentswitchertreeview.h" +#include "documentswitcheritem.h" #include "debug.h" -#include -#include -#include #include @@ -54,9 +49,17 @@ :KDevelop::IPlugin(QStringLiteral("kdevdocumentswitcher"), parent), view(nullptr) { setXMLFile(QStringLiteral("kdevdocumentswitcher.rc")); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "Adding active mainwindow from constructor" << KDevelop::ICore::self()->uiController()->activeMainWindow(); - addMainWindow( qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ) ); - connect( KDevelop::ICore::self()->uiController()->controller(), &Sublime::Controller::mainWindowAdded, this, &DocumentSwitcherPlugin::addMainWindow ); + qCDebug(PLUGIN_DOCUMENTSWITCHER) << "Adding active mainwindow from constructor"; + + KDevelop::IDocumentController *documentController = KDevelop::ICore::self()->documentController(); + for (KDevelop::IDocument *doc : documentController->openDocuments()) { + documentOpened(doc); + } + + // Signals to track last used documents. + connect(documentController, &KDevelop::IDocumentController::documentOpened, this, &DocumentSwitcherPlugin::documentOpened); + connect(documentController, &KDevelop::IDocumentController::documentActivated, this, &DocumentSwitcherPlugin::documentActivated); + connect(documentController, &KDevelop::IDocumentController::documentClosed, this, &DocumentSwitcherPlugin::documentClosed); #ifdef Q_OS_MACOS // Qt/Mac swaps the Ctrl and Meta (Command) keys by default, so that shortcuts defined as Ctrl+X @@ -132,16 +135,12 @@ void DocumentSwitcherPlugin::walk(const int from, const int to) { auto* window = qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ); - if( !window || !documentLists.contains( window ) || !documentLists[window].contains( window->area() ) ) - { - qCWarning(PLUGIN_DOCUMENTSWITCHER) << "This should not happen, tried to walk through document list of an unknown mainwindow!"; - return; - } + QModelIndex idx; const int step = from < to ? 1 : -1; if(!view->isVisible()) { - fillModel(window); + fillModel(); setViewGeometry(window); idx = model->index(from + step, 0); if(!idx.isValid()) { idx = model->index(0, 0); } @@ -160,50 +159,13 @@ void DocumentSwitcherPlugin::walkBackward() { walk(model->rowCount()-1, 0); } -void DocumentSwitcherPlugin::fillModel( Sublime::MainWindow* window ) +void DocumentSwitcherPlugin::fillModel() { model->clear(); - auto projectController = KDevelop::ICore::self()->projectController(); - const auto& views = documentLists[window][window->area()]; - for (Sublime::View* v : views) { - using namespace KDevelop; - Sublime::Document const* const slDoc = v->document(); - if( !slDoc ) - { - continue; - } - QString itemText = slDoc->title();// file name - IDocument const* const doc = qobject_cast(v->document()); - IProject* project = nullptr; - if( doc ) - { - QString path = projectController->prettyFilePath(doc->url(), - IProjectController::FormatPlain); - const bool isPartOfOpenProject = QDir::isRelativePath(path); - if (path.endsWith(QLatin1Char('/'))) { - path.chop(1); - } - if( isPartOfOpenProject ) - { - const int projectNameSize = path.indexOf(QLatin1Char(':')); - - // first: project name, second: path to file in project (might be just '/' when the file is in the project root dir) - const QPair fileInProjectInfo = (projectNameSize < 0) - ? qMakePair(path, QStringLiteral("/")) - : qMakePair(path.left(projectNameSize), path.mid(projectNameSize + 1)); - - itemText = QStringLiteral("%1 (%2:%3)").arg(itemText, - fileInProjectInfo.first, - fileInProjectInfo.second); - } else - { - itemText += QLatin1String(" (") + path + QLatin1Char(')'); - } - project = projectController->findProjectForUrl(doc->url()); - } - auto item = new QStandardItem( slDoc->icon(), itemText ); - item->setData(QVariant::fromValue(project), DocumentSwitcherTreeView::ProjectRole); - model->appendRow( item ); + + for (KDevelop::IDocument *doc : documentLists) { + DocumentSwitcherItem *item = new DocumentSwitcherItem(doc); + model->appendRow(item); } } @@ -224,80 +186,60 @@ { return; } - int row = view->selectionModel()->selectedRows().first().row(); + const int row = view->selectionModel()->selectedRows().first().row(); - auto* window = qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ); - Sublime::View* activatedView = nullptr; - if( window && documentLists.contains( window ) && documentLists[window].contains( window->area() ) ) + // Retrieve document from index + KDevelop::IDocument *activatedDocument = nullptr; + if( row >= 0 && row < documentLists.size() ) { - const QList l = documentLists[window][window->area()]; - if( row >= 0 && row < l.size() ) - { - activatedView = l.at( row ); - } + activatedDocument = documentLists.at( row ); } - if( activatedView ) { + + if( activatedDocument ) { + // Close document if( QApplication::mouseButtons() & Qt::MiddleButton ) { - window->area()->closeView( activatedView ); - fillModel( window ); + activatedDocument->close(); + fillModel(); if ( model->rowCount() == 0 ) { view->hide(); } else { view->selectionModel()->select( view->model()->index(0, 0), QItemSelectionModel::ClearAndSelect ); } - } else + } + // Activate document + else { - window->activateView( activatedView ); + KDevelop::IDocumentController *documentController = KDevelop::ICore::self()->documentController(); + documentController->activateDocument(activatedDocument); view->hide(); } } } -void DocumentSwitcherPlugin::unload() +void DocumentSwitcherPlugin::documentOpened(KDevelop::IDocument *document) { - const auto mainWindows = documentLists.keys(); - for (QObject* mw : mainWindows) { - removeMainWindow( mw ); + if (!documentLists.contains(document)) { + documentLists.prepend(document); } - delete forwardAction; - delete backwardAction; - view->deleteLater(); } - -void DocumentSwitcherPlugin::storeAreaViewList( Sublime::MainWindow* mainwindow, Sublime::Area* area ) +void DocumentSwitcherPlugin::documentActivated(KDevelop::IDocument *document) { - if( !documentLists.contains( mainwindow ) || !documentLists[mainwindow].contains(area) ) - { - QHash > areas; - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "adding area views for area:" << area << area->title() << "mainwindow:" << mainwindow << mainwindow->windowTitle(); - const auto views = area->views(); - for (Sublime::View* v : views) { - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "view:" << v << v->document()->title(); - } - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "done"; - areas.insert(area, views); - documentLists.insert( mainwindow, areas ); - } + documentLists.removeOne(document); + documentLists.prepend(document); +} +void DocumentSwitcherPlugin::documentClosed(KDevelop::IDocument *document) +{ + documentLists.removeOne(document); } -void DocumentSwitcherPlugin::addMainWindow( Sublime::MainWindow* mainwindow ) +void DocumentSwitcherPlugin::unload() { - if( !mainwindow ) { - return; - } - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "adding mainwindow:" << mainwindow << mainwindow->windowTitle(); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "storing all views from area:" << mainwindow->area()->title() << mainwindow->area(); - storeAreaViewList( mainwindow, mainwindow->area() ); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "connecting signals on mainwindow"; - connect( mainwindow, &Sublime::MainWindow::areaChanged, this, &DocumentSwitcherPlugin::changeArea ); - connect( mainwindow, &Sublime::MainWindow::activeViewChanged, this, &DocumentSwitcherPlugin::changeView ); - connect( mainwindow, &Sublime::MainWindow::viewAdded, this, &DocumentSwitcherPlugin::addView ); - connect( mainwindow, &Sublime::MainWindow::aboutToRemoveView, this, &DocumentSwitcherPlugin::removeView ); - connect( mainwindow, &Sublime::MainWindow::destroyed, this, &DocumentSwitcherPlugin::removeMainWindow); - mainwindow->installEventFilter( this ); + delete forwardAction; + delete backwardAction; + view->deleteLater(); } bool DocumentSwitcherPlugin::eventFilter( QObject* watched, QEvent* ev ) @@ -310,98 +252,11 @@ return QObject::eventFilter( watched, ev ); } -void DocumentSwitcherPlugin::addView( Sublime::View* view ) -{ - auto* mainwindow = qobject_cast( sender() ); - if( !mainwindow ) - return; - - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "got signal from mainwindow:" << mainwindow << mainwindow->windowTitle() - << "its area is:" << mainwindow->area() << mainwindow->area()->title() - << "adding view:" << view << view->document()->title(); - enableActions(); - documentLists[mainwindow][mainwindow->area()].append( view ); -} - void DocumentSwitcherPlugin::enableActions() { forwardAction->setEnabled(true); backwardAction->setEnabled(true); } - -void DocumentSwitcherPlugin::removeMainWindow( QObject* obj ) -{ - if( !obj || !documentLists.contains(obj) ) { - return; - } - obj->removeEventFilter( this ); - disconnect( obj, nullptr, this, nullptr ); - documentLists.remove( obj ); -} - - -void DocumentSwitcherPlugin::changeArea( Sublime::Area* area ) -{ - - auto* mainwindow = qobject_cast( sender() ); - Q_ASSERT( mainwindow ); - - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "area changed:" << area << area->title() << "mainwindow:" << mainwindow << mainwindow->windowTitle(); - - //Since the main-window only emits aboutToRemoveView for views within the current area, we must forget all areas except the active one - documentLists.remove(mainwindow); - - if( !documentLists[mainwindow].contains( area ) ) - { - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "got area change, storing its views"; - storeAreaViewList( mainwindow, area ); - } - enableActions(); -} -void DocumentSwitcherPlugin::changeView( Sublime::View* view ) -{ - if( !view ) - return; - - auto* mainwindow = qobject_cast( sender() ); - Q_ASSERT( mainwindow ); - - Sublime::Area* area = mainwindow->area(); - - int idx = documentLists[mainwindow][area].indexOf( view ); - if( idx != -1 ) - { - documentLists[mainwindow][area].removeAt( idx ); - } - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "moving view to front, list should now not contain this view anymore" << view << view->document()->title(); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "current area is:" << area << area->title() << "mainwindow:" << mainwindow << mainwindow->windowTitle(); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "idx of this view in list:" << documentLists[mainwindow][area].indexOf( view ); - documentLists[mainwindow][area].prepend( view ); - enableActions(); -} - -void DocumentSwitcherPlugin::removeView( Sublime::View* view ) -{ - if( !view ) - return; - - auto* mainwindow = qobject_cast( sender() ); - Q_ASSERT( mainwindow ); - - Sublime::Area* area = mainwindow->area(); - - int idx = documentLists[mainwindow][area].indexOf( view ); - if( idx != -1 ) - { - documentLists[mainwindow][area].removeAt( idx ); - } - - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "removing view, list should now not contain this view anymore" << view << view->document()->title(); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "current area is:" << area << area->title() << "mainwindow:" << mainwindow << mainwindow->windowTitle(); - qCDebug(PLUGIN_DOCUMENTSWITCHER) << "idx of this view in list:" << documentLists[mainwindow][area].indexOf( view ); - enableActions(); -} - #include "documentswitcherplugin.moc"