diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkCategoryItem.h b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkCategoryItem.h index a0d9c5dd66..a1765b0491 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkCategoryItem.h +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkCategoryItem.h @@ -1,28 +1,32 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkCategoryItem_h #define QmitkCategoryItem_h #include #include class QmitkCategoryItem : public QStandardItem { public: explicit QmitkCategoryItem(const QString& category); ~QmitkCategoryItem() override; + /** \brief Check if any view item child of this category item matches the given regular expression. + * + * \sa QmitkViewItem::Match() + */ bool HasMatch(const QRegularExpression& re) const; }; #endif diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewItem.h b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewItem.h index 74a51bb211..ff19471ca7 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewItem.h +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewItem.h @@ -1,41 +1,51 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkViewItem_h #define QmitkViewItem_h #include #include #include namespace berry { struct IViewDescriptor; } class QmitkViewItem : public QStandardItem { public: static constexpr int KeywordsRole = Qt::UserRole + 2; explicit QmitkViewItem(const berry::IViewDescriptor* view); ~QmitkViewItem() override; + /** \brief Enable or disable bold font for this item. + */ void SetBoldFont(bool enable); + /** \brief Get view keywords as optionally specified in a corresponding plugin.xml file. + */ QStringList GetKeywords() const; - void SetKeywords(const QStringList& keywords); + /** \brief Match item against regular expression. + * + * View name (text), description (tool tip), and keywords are considered. + */ bool Match(const QRegularExpression& re) const; + +private: + void SetKeywords(const QStringList& keywords); }; #endif diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.cpp b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.cpp index 8bc270c607..dfadb23077 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.cpp +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.cpp @@ -1,92 +1,96 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkViewModel.h" #include "QmitkCategoryItem.h" #include "QmitkViewItem.h" #include #include namespace { QString GetCategory(const berry::IViewDescriptor* view) { auto categoryPath = view->GetCategoryPath(); return !categoryPath.isEmpty() ? categoryPath.first() : ""; } } QmitkViewModel::QmitkViewModel(QObject* parent) : QStandardItemModel(parent) { const auto viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const auto views = viewRegistry->GetViews(); for (const auto& view : views) { + // Ignore internal views and self (View Navigator). if (view->IsInternal() || view->GetId() == "org.mitk.views.viewnavigator") continue; auto category = GetCategory(view.GetPointer()); + // If a view is not categorized, put it into a virtual "Other" category. if (category.isEmpty()) category = "Other"; + // Get corresponding category item or create it on the fly as needed. auto categoryItem = this->GetCategoryItem(category); if (categoryItem == nullptr) categoryItem = this->CreateCategoryItem(category); + // Add the new view item as child node to the corresponding category item. categoryItem->appendRow(new QmitkViewItem(view.GetPointer())); } } QmitkViewModel::~QmitkViewModel() { } QmitkCategoryItem* QmitkViewModel::CreateCategoryItem(const QString& category) { auto categoryItem = new QmitkCategoryItem(category); this->appendRow(categoryItem); return categoryItem; } QmitkCategoryItem* QmitkViewModel::GetCategoryItem(const QString& category) const { auto items = this->findItems(category); for (auto item : items) { auto categoryItem = dynamic_cast(item); if (categoryItem != nullptr) return categoryItem; } return nullptr; } QmitkViewItem* QmitkViewModel::GetViewItemFromId(const QString& id) const { const auto indices = this->match(this->index(0, 0), Qt::UserRole + 1, id, 1, Qt::MatchRecursive); return !indices.isEmpty() ? dynamic_cast(this->itemFromIndex(indices.first())) : nullptr; } diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.h b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.h index 8846f66ff6..fb0182ea73 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.h +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewModel.h @@ -1,34 +1,43 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkViewModel_h #define QmitkViewModel_h #include class QmitkCategoryItem; class QmitkViewItem; class QmitkViewModel : public QStandardItemModel { public: explicit QmitkViewModel(QObject* parent = nullptr); ~QmitkViewModel() override; + /** \brief Get category item by category name. + * + * \sa QmitkCategoryItem + */ QmitkCategoryItem* GetCategoryItem(const QString& category) const; + + /** \brief Get view item by view id. + * + * \sa QmitkViewItem + */ QmitkViewItem* GetViewItemFromId(const QString& id) const; private: QmitkCategoryItem* CreateCategoryItem(const QString& category); }; #endif diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.cpp b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.cpp index 6186760547..8250f4af2e 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.cpp @@ -1,138 +1,142 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkViewNavigatorView.h" #include "QmitkViewItem.h" #include "QmitkViewModel.h" #include "QmitkViewProxyModel.h" #include #include QmitkViewNavigatorView::QmitkViewNavigatorView() : m_Ui(new Ui::QmitkViewNavigatorView), m_Model(nullptr), m_ProxyModel(nullptr) { } QmitkViewNavigatorView::~QmitkViewNavigatorView() { this->GetPartService()->RemovePartListener(this); } void QmitkViewNavigatorView::CreateQtPartControl(QWidget* parent) { m_Ui->setupUi(parent); m_Model = new QmitkViewModel(parent); m_ProxyModel = new QmitkViewProxyModel(parent); m_ProxyModel->setSourceModel(m_Model); m_Ui->viewTreeView->setModel(m_ProxyModel); m_Ui->viewTreeView->expandAll(); connect(m_Ui->filterLineEdit, &QLineEdit::textChanged, this, &QmitkViewNavigatorView::OnFilterTextChanged); connect(m_Ui->viewTreeView, &QTreeView::doubleClicked, this, &QmitkViewNavigatorView::OnItemDoubleClicked); - this->GetPartService()->AddPartListener(this); + this->GetPartService()->AddPartListener(this); // The part service is not available earlier (e.g. in the constructor). } void QmitkViewNavigatorView::SetFocus() { m_Ui->filterLineEdit->setFocus(); } berry::IWorkbenchPage* QmitkViewNavigatorView::GetActivePage() const { if (auto site = this->GetSite(); site.IsNotNull()) { if (auto workbenchWindow = site->GetWorkbenchWindow(); workbenchWindow.IsNotNull()) return workbenchWindow->GetActivePage().GetPointer(); } return nullptr; } berry::IPartService* QmitkViewNavigatorView::GetPartService() const { if (auto site = this->GetSite(); site.IsNotNull()) { if (auto workbenchWindow = site->GetWorkbenchWindow(); workbenchWindow.IsNotNull()) return workbenchWindow->GetPartService(); } return nullptr; } -void QmitkViewNavigatorView::OnFilterTextChanged(const QString& pattern) +void QmitkViewNavigatorView::OnFilterTextChanged(const QString& filter) { - m_ProxyModel->setFilterWildcard(pattern); + m_ProxyModel->setFilterFixedString(filter); m_Ui->viewTreeView->expandAll(); } void QmitkViewNavigatorView::OnItemDoubleClicked(const QModelIndex& index) { auto viewItem = dynamic_cast(m_Model->itemFromIndex(m_ProxyModel->mapToSource(index))); if (viewItem != nullptr) { if (auto activePage = this->GetActivePage(); activePage != nullptr) { try { activePage->ShowView(viewItem->data().toString()); } catch (const berry::PartInitException& e) { MITK_ERROR << e.what(); } } } } berry::IPartListener::Events::Types QmitkViewNavigatorView::GetPartEventTypes() const { return Events::OPENED | Events::CLOSED; } void QmitkViewNavigatorView::PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() != "org.mitk.views.viewnavigator") { auto viewItem = m_Model->GetViewItemFromId(partRef->GetId()); if (viewItem != nullptr) viewItem->SetBoldFont(true); } else if (auto activePage = this->GetActivePage(); activePage != nullptr) { + // The active page is not available during view initialization. Hence, we hook + // into PartOpened() for the View Navigator itself, which is called shortly after, + // to initialize the state of all views. + for (const auto& view : activePage->GetViews()) { auto viewItem = m_Model->GetViewItemFromId(view->GetSite()->GetId()); if (viewItem != nullptr) viewItem->SetBoldFont(true); } } } void QmitkViewNavigatorView::PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef) { auto viewItem = m_Model->GetViewItemFromId(partRef->GetId()); if (viewItem != nullptr) viewItem->SetBoldFont(false); } diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.h b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.h index 2878040b99..0707968b67 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.h +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.h @@ -1,60 +1,60 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkViewNavigatorView_h #define QmitkViewNavigatorView_h #include #include class QmitkViewModel; class QmitkViewProxyModel; namespace berry { struct IPartService; struct IWorkbenchPage; } namespace Ui { class QmitkViewNavigatorView; } class QmitkViewNavigatorView : public QmitkAbstractView, public berry::IPartListener { Q_OBJECT public: QmitkViewNavigatorView(); ~QmitkViewNavigatorView() override; private: void CreateQtPartControl(QWidget* parent) override; void SetFocus() override; Events::Types GetPartEventTypes() const override; void PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef) override; void PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef) override; berry::IWorkbenchPage* GetActivePage() const; berry::IPartService* GetPartService() const; - void OnFilterTextChanged(const QString& pattern); + void OnFilterTextChanged(const QString& filter); void OnItemDoubleClicked(const QModelIndex& index); Ui::QmitkViewNavigatorView* m_Ui; QmitkViewModel* m_Model; QmitkViewProxyModel* m_ProxyModel; }; #endif diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.ui b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.ui index 12cdd7685f..d2792b28a5 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.ui +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewNavigatorView.ui @@ -1,55 +1,52 @@ QmitkViewNavigatorView 0 0 390 600 Form Filter: filterLineEdit true QAbstractItemView::NoEditTriggers - - true - true diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewProxyModel.cpp b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewProxyModel.cpp index 8076607839..dd0d2e99f3 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewProxyModel.cpp +++ b/Plugins/org.mitk.gui.qt.viewnavigator/src/internal/QmitkViewProxyModel.cpp @@ -1,63 +1,67 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkViewProxyModel.h" #include "QmitkCategoryItem.h" #include "QmitkViewItem.h" #include QmitkViewProxyModel::QmitkViewProxyModel(QObject* parent) : QSortFilterProxyModel(parent) { this->setFilterCaseSensitivity(Qt::CaseInsensitive); this->setSortCaseSensitivity(Qt::CaseInsensitive); } QmitkViewProxyModel::~QmitkViewProxyModel() { } bool QmitkViewProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { + // Primarily filter view items. Include their parent category items, though. + auto model = dynamic_cast(this->sourceModel()); if (model == nullptr) return true; const auto re = this->filterRegularExpression(); auto index = model->index(sourceRow, 0, sourceParent); const auto* item = model->itemFromIndex(index); if (auto viewItem = dynamic_cast(item); viewItem != nullptr) { return viewItem->Match(re); } else if (auto categoryItem = dynamic_cast(item); categoryItem != nullptr) { return categoryItem->HasMatch(re); } return true; } bool QmitkViewProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { + // Sort by item text in ascending order. + auto model = this->sourceModel(); - auto leftLabel = model->data(left).toString(); - auto rightLabel = model->data(right).toString(); + auto leftText = model->data(left).toString(); + auto rightText = model->data(right).toString(); auto caseSensitivity = this->sortCaseSensitivity(); - return leftLabel.compare(rightLabel, caseSensitivity) > 0; + return leftText.compare(rightText, caseSensitivity) > 0; }