diff --git a/Modules/QtWidgets/include/QmitkModelViewSelectionConnector.h b/Modules/QtWidgets/include/QmitkModelViewSelectionConnector.h index 380ff2f443..a7a623ddc4 100644 --- a/Modules/QtWidgets/include/QmitkModelViewSelectionConnector.h +++ b/Modules/QtWidgets/include/QmitkModelViewSelectionConnector.h @@ -1,156 +1,154 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKMODELVIEWSELECTIONCONNECTOR_H #define QMITKMODELVIEWSELECTIONCONNECTOR_H #include // qt widgets module #include // qt #include /** * @brief The 'QmitkModelViewSelectionConnector' is used to handle the selections of a model-view-pair. * * The class accepts a view and a model, which are used to react to selection changes. This class is able to propagate selection changes * to and receive from its surrounding class. * * The model-view-pair can be added as a selection listener to a selection service. This should be done by using 'AddPostSelectionListener' * with the existing selection service of the surrounding 'QmitkAbstractView'. * The model-view-pair can be set as a selection provider. This should be done by using 'SetAsSelectionProvider' with the existing * selection provider of the surrounding 'QmitkAbstractView'. * * The 'QmitkModelViewSelectionConnector' offers a public slot and signal that can be used to set / propagate the selected * nodes in the current view: * The 'SetCurrentSelection'-slot finds the indices of the given selected nodes in its internal data storage model and * changes the selection of the accordingly. * The 'CurrentSelectionChanged'-signal sends a list of selected nodes to it's environment. * The 'CurrentSelectionChanged'-signal is emitted by the 'ChangeModelSelection'-function, which transforms the internal item view's * selection into a data node list. The 'ChangeModelSelection'-function is called whenever the selection of the item view's * selection model changes. */ class MITKQTWIDGETS_EXPORT QmitkModelViewSelectionConnector : public QObject { Q_OBJECT public: QmitkModelViewSelectionConnector(); /** * @brief Set the view whose selection model is used to propagate or receive selection changes. Use the view's data model * to transform selected nodes into model indexes and vice versa. * * @pre The view's data model needs to be a 'QmitkAbstractDataStorageModel'. If so, the data model is received from * the view and stored as a private member. * The data model must return 'mitk::DataNode::Pointer' objects for model indexes if the role is 'QmitkDataNodeRole'. * @throw mitk::Exception, if the view is invalid or the view's data model is not a valid 'QmitkAbstractDataStorageModel'. * * @par view The view to set. */ void SetView(QAbstractItemView* view); /** * @brief Retrieve the currently selected nodes (equals the last CurrentSelectionChanged values). */ QList GetSelectedNodes() const; bool GetSelectOnlyVisibleNodes() const; Q_SIGNALS: /** * @brief A signal that will be emitted by the 'ChangeModelSelection'-function. This happens if the selection model * of the private member item view has changed. * * @par nodes A list of data nodes that are newly selected. */ void CurrentSelectionChanged(QList nodes); public Q_SLOTS: /** * @brief Change the selection modus of the item view's selection model. * * If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view. * An outgoing selection can then at most contain the filtered nodes. * If false, the incoming non-visible selection will be stored and later added to the outgoing selection, * to include the original selection that could not be modified. * The part of the original selection, that is non-visible are the nodes that are not * * @par selectOnlyVisibleNodes The bool value to define the selection modus. */ void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes); /** * @brief Transform a list of data nodes into a model selection and set this as a new selection of the * selection model of the private member item view. * * The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If * necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case * the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable * in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection * but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes'). * * @par nodes A list of data nodes that should be newly selected. */ void SetCurrentSelection(QList selectedNodes); private Q_SLOTS: /** * @brief Transform a model selection into a data node list and emit the 'CurrentSelectionChanged'-signal. * * The function adds the selected nodes from the original selection that could not be modified, if * 'm_SelectOnlyVisibleNodes' is false. * This slot is internally connected to the 'selectionChanged'-signal of the selection model of the private member item view. * * @par selected The newly selected items. * @par deselected The newly deselected items. */ void ChangeModelSelection(const QItemSelection& selected, const QItemSelection& deselected); private: QmitkAbstractDataStorageModel* m_Model; QAbstractItemView* m_View; bool m_SelectOnlyVisibleNodes; QList m_NonVisibleSelection; /* * @brief Retrieve the currently selected nodes from the selection model of the private member item view by * transforming the selection indexes into a data node list. * * In order to transform the indices into data nodes, the private data storage model must return * 'mitk::DataNode::Pointer' objects for model indexes if the role is QmitkDataNodeRole. */ QList GetInternalSelectedNodes() const; /* * @brief Filter the list of given nodes such that only those nodes are used that are valid * when using the data storage model's node predicate. * If no node predicate was set or the data storage model is invalid, the input list * of given nodes is returned. */ QList FilterNodeList(const QList& nodes) const; - /* - * @brief Return true, if the nodes in the list of given selected nodes are equal to the - * currently selected nodes from the selection model of the private member item view. - */ - bool IsEqualToCurrentSelection(QList& selectedNodes) const; - }; +/* +* @brief Return true, if the nodes in the list of two given selections are equal (Sorting is ignored. Any permutation is valid.)*/ +bool MITKQTWIDGETS_EXPORT EqualNodeSelections(const QList& selection1, const QList& selection2); + #endif // QMITKMODELVIEWSELECTIONCONNECTOR_H diff --git a/Modules/QtWidgets/src/QmitkModelViewSelectionConnector.cpp b/Modules/QtWidgets/src/QmitkModelViewSelectionConnector.cpp index d631536588..b582b9e240 100644 --- a/Modules/QtWidgets/src/QmitkModelViewSelectionConnector.cpp +++ b/Modules/QtWidgets/src/QmitkModelViewSelectionConnector.cpp @@ -1,191 +1,189 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // mitk gui qt common plugin #include "QmitkModelViewSelectionConnector.h" // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" QmitkModelViewSelectionConnector::QmitkModelViewSelectionConnector() : m_Model(nullptr) , m_View(nullptr) , m_SelectOnlyVisibleNodes(false) { // nothing here } void QmitkModelViewSelectionConnector::SetView(QAbstractItemView* view) { if (nullptr != m_View) { disconnect(m_View->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(ChangeModelSelection(const QItemSelection&, const QItemSelection&))); } // reset model-view pair and check for valid function argument m_View = nullptr; - m_Model = nullptr; if (nullptr == view) { mitkThrow() << "Invalid item view. To use the model-view selection connector please specify a valid 'QAbstractItemView'."; } - if (nullptr == dynamic_cast(view->model())) + auto storageModel = dynamic_cast(view->model()); + + if (storageModel == nullptr) { mitkThrow() << "Invalid data model. To use the model-view selection connector please set a valid 'QmitkAbstractDataStorageModel' for the given item view."; } // a valid item view and a valid data model was found m_View = view; - m_Model = dynamic_cast(m_View->model()); + m_Model = storageModel; connect(m_View->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(ChangeModelSelection(const QItemSelection&, const QItemSelection&))); } void QmitkModelViewSelectionConnector::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; } void QmitkModelViewSelectionConnector::SetCurrentSelection(QList selectedNodes) { if (nullptr == m_Model || nullptr == m_View) { return; } // filter input nodes and return the modified input node list QList filteredNodes = FilterNodeList(selectedNodes); - bool equal = IsEqualToCurrentSelection(filteredNodes); + bool equal = EqualNodeSelections(this->GetInternalSelectedNodes(), filteredNodes); if (equal) { return; } if (!m_SelectOnlyVisibleNodes) { // store the unmodified selection m_NonVisibleSelection = selectedNodes; // remove the nodes in the original selection that are already contained in the filtered input node list // this will keep the selection of the original nodes that are not presented by the current view unmodified, but allows to change the selection of the filtered nodes // later, the nodes of the 'm_NonVisibleSelection' member have to be added again to the list of selected (filtered) nodes, if a selection is sent from the current view (see 'ModelSelectionChanged') auto lambda = [&filteredNodes](mitk::DataNode::Pointer original) { return filteredNodes.contains(original); }; m_NonVisibleSelection.erase(std::remove_if(m_NonVisibleSelection.begin(), m_NonVisibleSelection.end(), lambda), m_NonVisibleSelection.end()); } // create new selection by retrieving the corresponding indices of the (filtered) nodes QItemSelection newCurrentSelection; for (const auto& node : filteredNodes) { QModelIndexList matched = m_Model->match(m_Model->index(0, 0), QmitkDataNodeRole, QVariant::fromValue(node), 1, Qt::MatchRecursive); if (!matched.empty()) { newCurrentSelection.select(matched.front(), matched.front()); } } m_View->selectionModel()->select(newCurrentSelection, QItemSelectionModel::ClearAndSelect); } void QmitkModelViewSelectionConnector::ChangeModelSelection(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/) { emit CurrentSelectionChanged(GetSelectedNodes()); } bool QmitkModelViewSelectionConnector::GetSelectOnlyVisibleNodes() const { return m_SelectOnlyVisibleNodes; } QList QmitkModelViewSelectionConnector::GetSelectedNodes() const { auto nodes = GetInternalSelectedNodes(); if (!m_SelectOnlyVisibleNodes) { // add the non-visible nodes from the original selection nodes.append(m_NonVisibleSelection); } return nodes; } QList QmitkModelViewSelectionConnector::GetInternalSelectedNodes() const { if (nullptr == m_Model || nullptr == m_View) { return QList(); } QList nodes; QModelIndexList selectedIndexes = m_View->selectionModel()->selectedIndexes(); for (const auto& index : selectedIndexes) { QVariant qvariantDataNode = m_Model->data(index, QmitkDataNodeRole); if (qvariantDataNode.canConvert()) { nodes.push_back(qvariantDataNode.value()); } } return nodes; } QList QmitkModelViewSelectionConnector::FilterNodeList(const QList& nodes) const { if (nodes.isEmpty()) { return QList(); } if (nullptr == m_Model) { return nodes; } mitk::NodePredicateBase* nodePredicate = m_Model->GetNodePredicate(); if (nullptr == nodePredicate) { // no filter set return nodes; } QList result; for (const auto& node : nodes) { if (true == nodePredicate->CheckNode(node)) { result.push_back(node); } } return result; } -bool QmitkModelViewSelectionConnector::IsEqualToCurrentSelection(QList& selectedNodes) const +bool MITKQTWIDGETS_EXPORT EqualNodeSelections(const QList& selection1, const QList& selection2) { - // get the currently selected nodes from the model - QList currentlySelectedNodes = GetInternalSelectedNodes(); - - if (currentlySelectedNodes.size() == selectedNodes.size()) + if (selection1.size() == selection2.size()) { // lambda to compare node pointer inside both lists auto lambda = [](mitk::DataNode::Pointer lhs, mitk::DataNode::Pointer rhs) { return lhs == rhs; }; - return std::is_permutation(selectedNodes.begin(), selectedNodes.end(), currentlySelectedNodes.begin(), currentlySelectedNodes.end(), lambda); + return std::is_permutation(selection1.begin(), selection1.end(), selection2.begin(), selection2.end(), lambda); } return false; }