diff --git a/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h b/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h index d6c6789868..935c5a37e3 100644 --- a/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h +++ b/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h @@ -1,269 +1,271 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 QMITKDATASTORAGETREEMODEL_H_ #define QMITKDATASTORAGETREEMODEL_H_ #include #include #include #include #include #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include #include #include /// \ingroup QmitkModule class MITKQTWIDGETS_EXPORT QmitkDataStorageTreeModel : public QAbstractItemModel { //# CONSTANTS,TYPEDEFS public: static const std::string COLUMN_NAME; static const std::string COLUMN_TYPE; static const std::string COLUMN_VISIBILITY; //# CTORS,DTOR public: QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage, bool _PlaceNewNodesOnTop = false, QObject *parent = 0); ~QmitkDataStorageTreeModel(); //# GETTER public: /// /// Get node at a specific model index. /// This function is used to get a node from a QModelIndex /// mitk::DataNode::Pointer GetNode(const QModelIndex &index) const; /// /// Returns a copy of the node-vector that is shown by this model /// virtual QList GetNodeSet() const; /// /// Get the DataStorage. /// const mitk::DataStorage::Pointer GetDataStorage() const; /// /// Get the top placement flag /// bool GetPlaceNewNodesOnTopFlag() { return m_PlaceNewNodesOnTop; } /// /// Set the top placement flag /// void SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop); //# (Re-)implemented from QAbstractItemModel //# Read model Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; //# hierarchical model /// /// called whenever the model or the view needs to create a QModelIndex for a particular /// child item (or a top-level item if parent is an invalid QModelIndex) /// QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; //# editable model bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override; bool dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; QStringList mimeTypes() const override; QMimeData *mimeData(const QModelIndexList &indexes) const override; static QMimeData *mimeDataFromModelIndexList(const QModelIndexList &indexes); //# End of QAbstractItemModel //# SETTER public: /// /// Sets the DataStorage. The whole model will be resetted. /// void SetDataStorage(mitk::DataStorage *_DataStorage); /// /// Notify that the DataStorage was deleted. The whole model will be resetted. /// - void SetDataStorageDeleted(const itk::Object *_DataStorage); + void SetDataStorageDeleted(); /// /// Adds a node to this model. /// If a predicate is set (not null) the node will be checked against it.The node has to have a data object (no one /// wants to see empty nodes). /// virtual void AddNode(const mitk::DataNode *node); /// /// Removes a node from this model. Also removes any event listener from the node. /// virtual void RemoveNode(const mitk::DataNode *node); /// /// Sets a node to modfified. Called by the DataStorage /// virtual void SetNodeModified(const mitk::DataNode *node); /// /// \return an index for the given datatreenode in the tree. If the node is not found /// QModelIndex GetIndex(const mitk::DataNode *) const; /// Set whether to allow hierarchy changes by dragging and dropping void SetAllowHierarchyChange(bool allowHierarchyChange); //# MISC protected: /// /// Helper class to represent a tree structure of DataNodes /// class TreeItem { public: /// /// Constructs a new TreeItem with the given DataNode (must not be 0) /// TreeItem(mitk::DataNode *_DataNode, TreeItem *_Parent = 0); /// /// Removes itself as child from its parent-> Does not delete its children /// \sa Delete() /// virtual ~TreeItem(); /// /// Find the index of an item /// int IndexOfChild(const TreeItem *item) const; /// /// \return The child at pos index or 0 if it not exists /// TreeItem *GetChild(int index) const; /// /// Find the TreeItem containing a special tree node (recursive tree function) /// TreeItem *Find(const mitk::DataNode *_DataNode) const; /// /// Get the amount of children /// int GetChildCount() const; /// /// \return the index of this node in its parent list /// int GetIndex() const; /// /// \return the parent of this tree item /// TreeItem *GetParent() const; /// /// Return the DataNode associated with this node /// mitk::DataNode::Pointer GetDataNode() const; /// /// Get all children as vector /// std::vector GetChildren() const; /// /// add another item as a child of this (only if not already in that list) /// void AddChild(TreeItem *item); /// /// remove another item as child from this /// void RemoveChild(TreeItem *item); /// /// inserts a child at the given position. if pos is not in range /// the element is added at the end /// void InsertChild(TreeItem *item, int index = -1); /// Sets the parent on the treeitem void SetParent(TreeItem *_Parent); /// /// Deletes the whole tree branch /// void Delete(); protected: TreeItem *m_Parent; std::vector m_Children; mitk::DataNode::Pointer m_DataNode; }; QList ToTreeItemPtrList(const QMimeData *mimeData); QList ToTreeItemPtrList(const QByteArray &ba); /// /// Adjusts the LayerProperty according to the nodes position /// void AdjustLayerProperty(); /// /// invoked after m_DataStorage or m_Predicate changed /// TreeItem *TreeItemFromIndex(const QModelIndex &index) const; /// /// Gives a ModelIndex for the Tree Item /// QModelIndex IndexFromTreeItem(TreeItem *) const; /// /// Returns the first element in the nodes sources list (if available) or 0 /// mitk::DataNode *GetParentNode(const mitk::DataNode *node) const; /// /// Adds all Childs in parent to vec. Before a child is added the function is called recursively /// void TreeToVector(TreeItem *parent, std::vector &vec) const; /// /// Adds all Childs in parent to vec. Before a child is added the function is called recursively /// void TreeToNodeSet(TreeItem *parent, QList &vec) const; /// /// Update Tree Model /// void Update(); //# ATTRIBUTES protected: mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::Pointer m_Predicate; bool m_PlaceNewNodesOnTop; TreeItem *m_Root; /// Flag to block the data storage events if nodes are added/removed by this class. bool m_BlockDataStorageEvents; /// This decides whether or not it is allowed to assign a different parent to a node /// If it is false, it is not possible to change the hierarchy of nodes by dragging /// and dropping. /// If it is true, dragging nodes on another node will replace all of their parents /// with that one. bool m_AllowHierarchyChange; private: void AddNodeInternal(const mitk::DataNode *); void RemoveNodeInternal(const mitk::DataNode *); /// /// Checks if dicom properties patient name, study names and series name exists /// bool DicomPropertiesExists(const mitk::DataNode &) const; + + unsigned long m_DataStorageDeletedTag; }; #endif /* QMITKDATASTORAGETREEMODEL_H_ */ diff --git a/Modules/QtWidgets/include/QmitkPropertiesTableModel.h b/Modules/QtWidgets/include/QmitkPropertiesTableModel.h index 258e3965de..7e4426afa9 100644 --- a/Modules/QtWidgets/include/QmitkPropertiesTableModel.h +++ b/Modules/QtWidgets/include/QmitkPropertiesTableModel.h @@ -1,251 +1,253 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ /// Header guard. #ifndef QmitkPropertiesTableModel_h #define QmitkPropertiesTableModel_h #include //# Own includes #include "mitkDataNode.h" #include "mitkWeakPointer.h" //# Toolkit includes #include #include #include //# Forward declarations /** * \ingroup QmitkModule * \brief A table model for showing and editing mitk::Properties. * * \see QmitkPropertyDelegate */ class MITKQTWIDGETS_EXPORT QmitkPropertiesTableModel : public QAbstractTableModel { //# PUBLIC CTORS,DTOR,TYPEDEFS,CONSTANTS public: static const int PROPERTY_NAME_COLUMN = 0; static const int PROPERTY_VALUE_COLUMN = 1; /// /// Typedef for the complete Property Datastructure, which may be written as follows: /// Name->(mitk::BaseProperty::Pointer) /// typedef std::pair PropertyDataSet; /// /// Constructs a new QmitkDataStorageTableModel /// and sets the DataNode for this TableModel. QmitkPropertiesTableModel(QObject *parent = nullptr, mitk::PropertyList::Pointer _PropertyList = nullptr); /// /// Standard dtor. Nothing to do here. virtual ~QmitkPropertiesTableModel(); //# PUBLIC GETTER public: /// /// Returns the property list of this table model. /// mitk::PropertyList::Pointer GetPropertyList() const; /// /// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...) Qt::ItemFlags flags(const QModelIndex &index) const override; /// /// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...) QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; /// /// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...) QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; /// /// Overwritten from QAbstractTableModel. Returns the flags what can be done with the items (view, edit, ...) int rowCount(const QModelIndex &parent = QModelIndex()) const override; /// /// Overwritten from QAbstractTableModel. Returns the number of columns. That is usually two in this model: /// the properties name and its value. int columnCount(const QModelIndex &parent) const override; //# PUBLIC SETTER public: /// /// Sets the Property List to show. Resets the whole model. If _PropertyList is nullptr the model is empty. /// void SetPropertyList(mitk::PropertyList *_PropertyList); /// /// \brief Gets called when the list is about to be deleted. /// - virtual void PropertyListDelete(const itk::Object *_PropertyList); + virtual void PropertyListDelete(); /// /// \brief Called when a single property was changed. Send a model changed event to the Qt-outer world. /// virtual void PropertyModified(const itk::Object *caller, const itk::EventObject &event); /// /// \brief Called when a single property was changed. Send a model changed event to the Qt-outer world. /// virtual void PropertyDelete(const itk::Object *caller, const itk::EventObject &event); /// /// \brief Set a keyword for filtering of properties. Only properties beginning with this string will be shown /// virtual void SetFilterPropertiesKeyWord(std::string _FilterKeyWord); /// /// Overridden from QAbstractTableModel. Sets data at index for given role. /// bool setData(const QModelIndex &index, const QVariant &value, int role) override; /// /// \brief Reimplemented sort function from QAbstractTableModel to enable sorting on the table. /// void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; //#PROTECTED INNER CLASSES protected: /// /// \struct PropertyDataSetCompareFunction /// \brief A struct that inherits from std::binary_function. You can use it in std::sort algorithm for sorting the /// property list elements. /// struct PropertyDataSetCompareFunction : public std::binary_function { /// /// \brief Specifies field of the property with which it will be sorted. /// enum CompareCriteria { CompareByName = 0, CompareByValue }; /// /// \brief Specifies Ascending/descending ordering. /// enum CompareOperator { Less = 0, Greater }; /// /// \brief Creates a PropertyDataSetCompareFunction. A CompareCriteria and a CompareOperator must be given. /// PropertyDataSetCompareFunction(CompareCriteria _CompareCriteria = CompareByName, CompareOperator _CompareOperator = Less); /// /// \brief The reimplemented compare function. /// bool operator()(const PropertyDataSet &_Left, const PropertyDataSet &_Right) const; protected: CompareCriteria m_CompareCriteria; CompareOperator m_CompareOperator; }; /// /// An unary function for selecting Properties in a vector by a key word. /// struct PropertyListElementFilterFunction : public std::unary_function { PropertyListElementFilterFunction(const std::string &m_FilterKeyWord); /// /// \brief The reimplemented compare function. /// bool operator()(const PropertyDataSet &_Elem) const; protected: std::string m_FilterKeyWord; }; //# PROTECTED GETTER protected: /// /// \brief Searches for the specified property and returns the row of the element in this QTableModel. /// If any errors occur, the function returns -1. /// int FindProperty(const mitk::BaseProperty *_Property) const; //# PROTECTED SETTER protected: /// /// Adds a property dataset to the current selection. /// When a property is added a modified and delete listener /// is appended. /// void AddSelectedProperty(PropertyDataSet &_PropertyDataSet); /// /// Removes a property dataset from the current selection. /// When a property is removed the modified and delete listener /// are also removed. /// void RemoveSelectedProperty(unsigned int _Index); /// /// Reset is called when a new filter keyword is set or a new /// PropertyList is set. First of all, all priorly selected /// properties are removed. Then all properties to be /// selected (specified by the keyword) are added to the selection. /// void Reset(); //# PROTECTED MEMBERS protected: /// /// Holds the pointer to the properties list. Dont use smart pointers here. Instead: Listen /// to the delete event. mitk::WeakPointer m_PropertyList; /// /// Store the properties in a vector so that they may be sorted std::vector m_SelectedProperties; /// /// \brief Holds all tags of Modified Event Listeners. We need it to remove them again. /// std::vector m_PropertyModifiedObserverTags; /// /// \brief Holds all tags of Modified Event Listeners. We need it to remove them again. /// std::vector m_PropertyDeleteObserverTags; + unsigned long m_PropertyListDeleteObserverTag; + /// /// \brief Indicates if this class should neglect all incoming events because /// the class itself triggered the event (e.g. when a property was edited). /// bool m_BlockEvents; /// /// \brief The property is true when the property list is sorted in descending order. /// bool m_SortDescending; /// /// \brief If set to any value, only properties containing the specified keyword in their name will be shown. /// std::string m_FilterKeyWord; }; #endif /* QMITKPROPERTIESTABLEMODEL_H_ */ diff --git a/Modules/QtWidgets/include/QmitkPropertyItemModel.h b/Modules/QtWidgets/include/QmitkPropertyItemModel.h index 803048e442..333e7f691f 100644 --- a/Modules/QtWidgets/include/QmitkPropertyItemModel.h +++ b/Modules/QtWidgets/include/QmitkPropertyItemModel.h @@ -1,88 +1,89 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 QmitkPropertyItemModel_h #define QmitkPropertyItemModel_h #include #include #include #include class QmitkPropertyItem; namespace berry { struct IBerryPreferences; } namespace mitk { class IPropertyAliases; class IPropertyFilters; enum { PropertyRole = Qt::UserRole + 1 }; } class MITKQTWIDGETS_EXPORT QmitkPropertyItemModel : public QAbstractItemModel { Q_OBJECT public: explicit QmitkPropertyItemModel(QObject *parent = nullptr); ~QmitkPropertyItemModel() override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; mitk::PropertyList *GetPropertyList() const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; void OnPreferencesChanged(); QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; void SetPropertyList(mitk::PropertyList *propertyList, const QString &className = ""); void Update(); void SetShowAliases(const bool showAliases) { this->m_ShowAliases = showAliases; } bool GetShowAliases() const { return this->m_ShowAliases; } void SetFilterProperties(const bool filterProperties) { this->m_FilterProperties = filterProperties; } bool GetFilterProperties() const { return this->m_FilterProperties; } private: void CreateRootItem(); QModelIndex FindProperty(const mitk::BaseProperty *property); - void OnPropertyListModified(const itk::Object *propertyList); - void OnPropertyListDeleted(const itk::Object *propertyList); - void OnPropertyDeleted(const itk::Object *property, const itk::EventObject &event); + void OnPropertyListModified(); + void OnPropertyListDeleted(); void OnPropertyModified(const itk::Object *property, const itk::EventObject &event); - void SetNewPropertyList(mitk::PropertyList *propertyList); + void SetNewPropertyList(mitk::PropertyList *newPropertyList); bool m_ShowAliases; bool m_FilterProperties; mitk::IPropertyAliases *m_PropertyAliases; mitk::IPropertyFilters *m_PropertyFilters; mitk::WeakPointer m_PropertyList; QString m_ClassName; std::unique_ptr m_RootItem; std::map m_PropertyDeletedTags; std::map m_PropertyModifiedTags; + unsigned long m_PropertyListDeletedTag; + unsigned long m_PropertyListModifiedTag; }; #endif diff --git a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp index b99c4a9d2b..a5b633993d 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp @@ -1,1041 +1,1041 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "QmitkDataStorageTreeModel.h" #include "QmitkNodeDescriptorManager.h" #include #include #include #include #include #include #include #include #include QmitkDataStorageTreeModel::QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage, bool _PlaceNewNodesOnTop, QObject *parent) : QAbstractItemModel(parent), m_DataStorage(0), m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop), m_Root(0), m_BlockDataStorageEvents(false), m_AllowHierarchyChange(false) { this->SetDataStorage(_DataStorage); } QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel() { // set data storage to 0 = remove all listeners this->SetDataStorage(0); m_Root->Delete(); m_Root = 0; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode(const QModelIndex &index) const { return this->TreeItemFromIndex(index)->GetDataNode(); } const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const { return m_DataStorage.Lock(); } QModelIndex QmitkDataStorageTreeModel::index(int row, int column, const QModelIndex &parent) const { TreeItem *parentItem; if (!parent.isValid()) parentItem = m_Root; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->GetChild(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } int QmitkDataStorageTreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentTreeItem = this->TreeItemFromIndex(parent); return parentTreeItem->GetChildCount(); } Qt::ItemFlags QmitkDataStorageTreeModel::flags(const QModelIndex &index) const { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if (index.isValid()) { if (DicomPropertiesExists(*dataNode)) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } else { return Qt::ItemIsDropEnabled; } } int QmitkDataStorageTreeModel::columnCount(const QModelIndex & /* parent = QModelIndex() */) const { return 1; } QModelIndex QmitkDataStorageTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = this->TreeItemFromIndex(index); TreeItem *parentItem = childItem->GetParent(); if (parentItem == m_Root) return QModelIndex(); return this->createIndex(parentItem->GetIndex(), 0, parentItem); } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItemFromIndex(const QModelIndex &index) const { if (index.isValid()) return static_cast(index.internalPointer()); else return m_Root; } Qt::DropActions QmitkDataStorageTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkDataStorageTreeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } bool QmitkDataStorageTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent) { // Early exit, returning true, but not actually doing anything (ignoring data). if (action == Qt::IgnoreAction) { return true; } // Note, we are returning true if we handled it, and false otherwise bool returnValue = false; if (data->hasFormat("application/x-qabstractitemmodeldatalist")) { returnValue = true; // First we extract a Qlist of TreeItem* pointers. QList listOfItemsToDrop = ToTreeItemPtrList(data); if (listOfItemsToDrop.empty()) { return false; } // Retrieve the TreeItem* where we are dropping stuff, and its parent. TreeItem *dropItem = this->TreeItemFromIndex(parent); TreeItem *parentItem = dropItem->GetParent(); // If item was dropped onto empty space, we select the root node if (dropItem == m_Root) { parentItem = m_Root; } // Dragging and Dropping is only allowed within the same parent, so use the first item in list to validate. // (otherwise, you could have a derived image such as a segmentation, and assign it to another image). // NOTE: We are assuming the input list is valid... i.e. when it was dragged, all the items had the same parent. // Determine whether or not the drag and drop operation is a valid one. // Examples of invalid operations include: // - dragging nodes with different parents // - dragging nodes from one parent to another parent, if m_AllowHierarchyChange is false // - dragging a node on one of its child nodes (only relevant if m_AllowHierarchyChange is true) bool isValidDragAndDropOperation(true); // different parents { TreeItem *firstParent = listOfItemsToDrop[0]->GetParent(); QList::iterator diIter; for (diIter = listOfItemsToDrop.begin() + 1; diIter != listOfItemsToDrop.end(); diIter++) { if (firstParent != (*diIter)->GetParent()) { isValidDragAndDropOperation = false; break; } } } // dragging from one parent to another if ((!m_AllowHierarchyChange) && isValidDragAndDropOperation) { if (row == -1) // drag onto a node { isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == parentItem; } else // drag between nodes { isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == dropItem; } } // dragging on a child node of one the dragged nodes { QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { TreeItem *tempItem = dropItem; while (tempItem != m_Root) { tempItem = tempItem->GetParent(); if (tempItem == *diIter) { isValidDragAndDropOperation = false; } } } } if (!isValidDragAndDropOperation) return isValidDragAndDropOperation; if (listOfItemsToDrop[0] != dropItem && isValidDragAndDropOperation) { // Retrieve the index of where we are dropping stuff. QModelIndex parentModelIndex = this->IndexFromTreeItem(parentItem); int dragIndex = 0; // Iterate through the list of TreeItem (which may be at non-consecutive indexes). QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { TreeItem *itemToDrop = *diIter; // if the item is dragged down we have to compensate its final position for the // fact it is deleted lateron, this only applies if it is dragged within the same level if ((itemToDrop->GetIndex() < row) && (itemToDrop->GetParent() == dropItem)) { dragIndex = 1; } // Here we assume that as you remove items, one at a time, that GetIndex() will be valid. this->beginRemoveRows( this->IndexFromTreeItem(itemToDrop->GetParent()), itemToDrop->GetIndex(), itemToDrop->GetIndex()); itemToDrop->GetParent()->RemoveChild(itemToDrop); this->endRemoveRows(); } // row = -1 dropped on an item, row != -1 dropped in between two items // Select the target index position, or put it at the end of the list. int dropIndex = 0; if (row != -1) { if (dragIndex == 0) dropIndex = std::min(row, parentItem->GetChildCount() - 1); else dropIndex = std::min(row - 1, parentItem->GetChildCount() - 1); } else { dropIndex = dropItem->GetIndex(); } QModelIndex dropItemModelIndex = this->IndexFromTreeItem(dropItem); if ((row == -1 && dropItemModelIndex.row() == -1) || dropItemModelIndex.row() > parentItem->GetChildCount()) dropIndex = parentItem->GetChildCount() - 1; // Now insert items again at the drop item position if (m_AllowHierarchyChange) { this->beginInsertRows(dropItemModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); } else { this->beginInsertRows(parentModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); } for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { // dropped on node, behaviour depends on preference setting if (m_AllowHierarchyChange) { auto dataStorage = m_DataStorage.Lock(); m_BlockDataStorageEvents = true; mitk::DataNode *droppedNode = (*diIter)->GetDataNode(); mitk::DataNode *dropOntoNode = dropItem->GetDataNode(); dataStorage->Remove(droppedNode); dataStorage->Add(droppedNode, dropOntoNode); m_BlockDataStorageEvents = false; dropItem->InsertChild((*diIter), dropIndex); } else { if (row == -1) // drag onto a node { parentItem->InsertChild((*diIter), dropIndex); } else // drag between nodes { dropItem->InsertChild((*diIter), dropIndex); } } dropIndex++; } this->endInsertRows(); // Change Layers to match. this->AdjustLayerProperty(); } } else if (data->hasFormat("application/x-mitk-datanodes")) { returnValue = true; int numberOfNodesDropped = 0; QList dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); mitk::DataNode *node = nullptr; foreach (node, dataNodeList) { if (node && !m_DataStorage.IsExpired() && !m_DataStorage.Lock()->Exists(node)) { m_DataStorage.Lock()->Add(node); mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); numberOfNodesDropped++; } } } // Only do a rendering update, if we actually dropped anything. if (numberOfNodesDropped > 0) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return returnValue; } QStringList QmitkDataStorageTreeModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << "application/x-qabstractitemmodeldatalist"; types << "application/x-mitk-datanodes"; return types; } QMimeData *QmitkDataStorageTreeModel::mimeData(const QModelIndexList &indexes) const { return mimeDataFromModelIndexList(indexes); } QMimeData *QmitkDataStorageTreeModel::mimeDataFromModelIndexList(const QModelIndexList &indexes) { QMimeData *ret = new QMimeData; QString treeItemAddresses(""); QString dataNodeAddresses(""); QByteArray baTreeItemPtrs; QByteArray baDataNodePtrs; QDataStream dsTreeItemPtrs(&baTreeItemPtrs, QIODevice::WriteOnly); QDataStream dsDataNodePtrs(&baDataNodePtrs, QIODevice::WriteOnly); for (int i = 0; i < indexes.size(); i++) { TreeItem *treeItem = static_cast(indexes.at(i).internalPointer()); dsTreeItemPtrs << reinterpret_cast(treeItem); dsDataNodePtrs << reinterpret_cast(treeItem->GetDataNode().GetPointer()); // --------------- deprecated ----------------- unsigned long long treeItemAddress = reinterpret_cast(treeItem); unsigned long long dataNodeAddress = reinterpret_cast(treeItem->GetDataNode().GetPointer()); QTextStream(&treeItemAddresses) << treeItemAddress; QTextStream(&dataNodeAddresses) << dataNodeAddress; if (i != indexes.size() - 1) { QTextStream(&treeItemAddresses) << ","; QTextStream(&dataNodeAddresses) << ","; } // -------------- end deprecated ------------- } // ------------------ deprecated ----------------- ret->setData("application/x-qabstractitemmodeldatalist", QByteArray(treeItemAddresses.toLatin1())); ret->setData("application/x-mitk-datanodes", QByteArray(dataNodeAddresses.toLatin1())); // --------------- end deprecated ----------------- ret->setData(QmitkMimeTypes::DataStorageTreeItemPtrs, baTreeItemPtrs); ret->setData(QmitkMimeTypes::DataNodePtrs, baDataNodePtrs); return ret; } QVariant QmitkDataStorageTreeModel::data(const QModelIndex &index, int role) const { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); // get name of treeItem (may also be edited) QString nodeName; if (DicomPropertiesExists(*dataNode)) { mitk::BaseProperty *seriesDescription = (dataNode->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str())); mitk::BaseProperty *studyDescription = (dataNode->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str())); mitk::BaseProperty *patientsName = (dataNode->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str())); mitk::BaseProperty *seriesDescription_deprecated = (dataNode->GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty *studyDescription_deprecated = (dataNode->GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty *patientsName_deprecated = (dataNode->GetProperty("dicom.patient.PatientsName")); if (patientsName) { nodeName += QString::fromStdString(patientsName->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(studyDescription->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(seriesDescription->GetValueAsString()); } else { /** Code coveres the deprecated property naming for backwards compatibility */ nodeName += QString::fromStdString(patientsName_deprecated->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(studyDescription_deprecated->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(seriesDescription_deprecated->GetValueAsString()); } } else { nodeName = QString::fromStdString(dataNode->GetName()); } if (nodeName.isEmpty()) { nodeName = "unnamed"; } if (role == Qt::DisplayRole) return nodeName; else if (role == Qt::ToolTipRole) return nodeName; else if (role == Qt::DecorationRole) { QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (role == Qt::CheckStateRole) { return dataNode->IsVisible(0); } else if (role == QmitkDataNodeRole) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (role == QmitkDataNodeRawPointerRole) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageTreeModel::DicomPropertiesExists(const mitk::DataNode &node) const { bool propertiesExists = false; mitk::BaseProperty *seriesDescription_deprecated = (node.GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty *studyDescription_deprecated = (node.GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty *patientsName_deprecated = (node.GetProperty("dicom.patient.PatientsName")); mitk::BaseProperty *seriesDescription = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str())); mitk::BaseProperty *studyDescription = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str())); mitk::BaseProperty *patientsName = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str())); if (patientsName != nullptr && studyDescription != nullptr && seriesDescription != nullptr) { if ((!patientsName->GetValueAsString().empty()) && (!studyDescription->GetValueAsString().empty()) && (!seriesDescription->GetValueAsString().empty())) { propertiesExists = true; } } /** Code coveres the deprecated property naming for backwards compatibility */ if (patientsName_deprecated != nullptr && studyDescription_deprecated != nullptr && seriesDescription_deprecated != nullptr) { if ((!patientsName_deprecated->GetValueAsString().empty()) && (!studyDescription_deprecated->GetValueAsString().empty()) && (!seriesDescription_deprecated->GetValueAsString().empty())) { propertiesExists = true; } } return propertiesExists; } QVariant QmitkDataStorageTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root) return QString::fromStdString(m_Root->GetDataNode()->GetName()); return QVariant(); } void QmitkDataStorageTreeModel::SetDataStorage(mitk::DataStorage *_DataStorage) { if (m_DataStorage != _DataStorage) // dont take the same again { if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); // remove Listener for the data storage itself - m_DataStorage.ObjectDelete.RemoveListener(mitk::MessageDelegate1( - this, &QmitkDataStorageTreeModel::SetDataStorageDeleted)); + dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove listeners for the nodes dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageTreeModel::AddNode)); dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); } // take over the new data storage m_DataStorage = _DataStorage; // delete the old root (if necessary, create new) if (m_Root) m_Root->Delete(); mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Manager"); m_Root = new TreeItem(rootDataNode, 0); this->beginResetModel(); this->endResetModel(); if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); // add Listener for the data storage itself - m_DataStorage.ObjectDelete.AddListener(mitk::MessageDelegate1( - this, &QmitkDataStorageTreeModel::SetDataStorageDeleted)); + auto command = itk::SimpleMemberCommand::New(); + command->SetCallbackFunction(this, &QmitkDataStorageTreeModel::SetDataStorageDeleted); + m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command); // add listeners for the nodes dataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::AddNode)); dataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = dataStorage->GetSubset(m_Predicate); // finally add all nodes to the model this->Update(); } } } -void QmitkDataStorageTreeModel::SetDataStorageDeleted(const itk::Object * /*_DataStorage*/) +void QmitkDataStorageTreeModel::SetDataStorageDeleted() { this->SetDataStorage(0); } void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node) { if (node == 0 || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != 0) return; // find out if we have a root node TreeItem *parentTreeItem = m_Root; QModelIndex index; mitk::DataNode *parentDataNode = this->GetParentNode(node); if (parentDataNode) // no top level data node { parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item if (!parentTreeItem) { this->AddNode(parentDataNode); parentTreeItem = m_Root->Find(parentDataNode); if (!parentTreeItem) return; } // get the index of this parent with the help of the grand parent index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem); } // add node if (m_PlaceNewNodesOnTop) { // emit beginInsertRows event beginInsertRows(index, 0, 0); parentTreeItem->InsertChild(new TreeItem(const_cast(node)), 0); } else { int firstRowWithASiblingBelow = 0; int nodeLayer = -1; node->GetIntProperty("layer", nodeLayer); for (TreeItem* siblingTreeItem: parentTreeItem->GetChildren()) { int siblingLayer = -1; if (mitk::DataNode* siblingNode = siblingTreeItem->GetDataNode()) { siblingNode->GetIntProperty("layer", siblingLayer); } if (nodeLayer > siblingLayer) { break; } ++firstRowWithASiblingBelow; } beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); parentTreeItem->InsertChild(new TreeItem(const_cast(node)), firstRowWithASiblingBelow); } // emit endInsertRows event endInsertRows(); if(m_PlaceNewNodesOnTop) { this->AdjustLayerProperty(); } } void QmitkDataStorageTreeModel::AddNode(const mitk::DataNode *node) { if (node == 0 || m_BlockDataStorageEvents || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != 0) return; this->AddNodeInternal(node); } void QmitkDataStorageTreeModel::SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop) { m_PlaceNewNodesOnTop = _PlaceNewNodesOnTop; } void QmitkDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode *node) { if (!m_Root) return; TreeItem *treeItem = m_Root->Find(node); if (!treeItem) return; // return because there is no treeitem containing this node TreeItem *parentTreeItem = treeItem->GetParent(); QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem); // emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model) this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex()); // remove node std::vector children = treeItem->GetChildren(); delete treeItem; // emit endRemoveRows event endRemoveRows(); // move all children of deleted node into its parent for (std::vector::iterator it = children.begin(); it != children.end(); it++) { // emit beginInsertRows event beginInsertRows(parentIndex, parentTreeItem->GetChildCount(), parentTreeItem->GetChildCount()); // add nodes again parentTreeItem->AddChild(*it); // emit endInsertRows event endInsertRows(); } this->AdjustLayerProperty(); } void QmitkDataStorageTreeModel::RemoveNode(const mitk::DataNode *node) { if (node == 0 || m_BlockDataStorageEvents) return; this->RemoveNodeInternal(node); } void QmitkDataStorageTreeModel::SetNodeModified(const mitk::DataNode *node) { TreeItem *treeItem = m_Root->Find(node); if (treeItem) { TreeItem *parentTreeItem = treeItem->GetParent(); // as the root node should not be removed one should always have a parent item if (!parentTreeItem) return; QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem); // now emit the dataChanged signal emit dataChanged(index, index); } } mitk::DataNode *QmitkDataStorageTreeModel::GetParentNode(const mitk::DataNode *node) const { mitk::DataNode *dataNode = 0; mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage.Lock()->GetSources(node); if (_Sources->Size() > 0) dataNode = _Sources->front(); return dataNode; } bool QmitkDataStorageTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if (!dataNode) return false; if (role == Qt::EditRole && !value.toString().isEmpty()) { dataNode->SetStringProperty("name", value.toString().toStdString().c_str()); mitk::PlanarFigure *planarFigure = dynamic_cast(dataNode->GetData()); if (planarFigure != nullptr) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (role == Qt::CheckStateRole) { // Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element. // Therefore the checkstate is being estimated again here. QVariant qcheckstate = index.data(Qt::CheckStateRole); int checkstate = qcheckstate.toInt(); bool isVisible = bool(checkstate); dataNode->SetVisibility(!isVisible); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } // inform listeners about changes emit dataChanged(index, index); return true; } bool QmitkDataStorageTreeModel::setHeaderData(int /*section*/, Qt::Orientation /*orientation*/, const QVariant & /* value */, int /*role = Qt::EditRole*/) { return false; } void QmitkDataStorageTreeModel::AdjustLayerProperty() { /// transform the tree into an array and set the layer property descending std::vector vec; this->TreeToVector(m_Root, vec); int i = vec.size() - 1; for (std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { mitk::DataNode::Pointer dataNode = (*it)->GetDataNode(); bool fixedLayer = false; if (!(dataNode->GetBoolProperty("fixedLayer", fixedLayer) && fixedLayer)) dataNode->SetIntProperty("layer", i); --i; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataStorageTreeModel::TreeToVector(TreeItem *parent, std::vector &vec) const { TreeItem *current; for (int i = 0; i < parent->GetChildCount(); ++i) { current = parent->GetChild(i); this->TreeToVector(current, vec); vec.push_back(current); } } QModelIndex QmitkDataStorageTreeModel::IndexFromTreeItem(TreeItem *item) const { if (item == m_Root) return QModelIndex(); else return this->createIndex(item->GetIndex(), 0, item); } QList QmitkDataStorageTreeModel::GetNodeSet() const { QList res; if (m_Root) this->TreeToNodeSet(m_Root, res); return res; } void QmitkDataStorageTreeModel::TreeToNodeSet(TreeItem *parent, QList &vec) const { TreeItem *current; for (int i = 0; i < parent->GetChildCount(); ++i) { current = parent->GetChild(i); vec.push_back(current->GetDataNode()); this->TreeToNodeSet(current, vec); } } QModelIndex QmitkDataStorageTreeModel::GetIndex(const mitk::DataNode *node) const { if (m_Root) { TreeItem *item = m_Root->Find(node); if (item) return this->IndexFromTreeItem(item); } return QModelIndex(); } QList QmitkDataStorageTreeModel::ToTreeItemPtrList(const QMimeData *mimeData) { if (mimeData == nullptr || !mimeData->hasFormat(QmitkMimeTypes::DataStorageTreeItemPtrs)) { return QList(); } return ToTreeItemPtrList(mimeData->data(QmitkMimeTypes::DataStorageTreeItemPtrs)); } QList QmitkDataStorageTreeModel::ToTreeItemPtrList(const QByteArray &ba) { QList result; QDataStream ds(ba); while (!ds.atEnd()) { quintptr treeItemPtr; ds >> treeItemPtr; result.push_back(reinterpret_cast(treeItemPtr)); } return result; } QmitkDataStorageTreeModel::TreeItem::TreeItem(mitk::DataNode *_DataNode, TreeItem *_Parent) : m_Parent(_Parent), m_DataNode(_DataNode) { if (m_Parent) m_Parent->AddChild(this); } QmitkDataStorageTreeModel::TreeItem::~TreeItem() { if (m_Parent) m_Parent->RemoveChild(this); } void QmitkDataStorageTreeModel::TreeItem::Delete() { while (m_Children.size() > 0) delete m_Children.back(); delete this; } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItem::Find(const mitk::DataNode *_DataNode) const { QmitkDataStorageTreeModel::TreeItem *item = 0; if (_DataNode) { if (m_DataNode == _DataNode) item = const_cast(this); else { for (std::vector::const_iterator it = m_Children.begin(); it != m_Children.end(); ++it) { if (item) break; item = (*it)->Find(_DataNode); } } } return item; } int QmitkDataStorageTreeModel::TreeItem::IndexOfChild(const TreeItem *item) const { std::vector::const_iterator it = std::find(m_Children.begin(), m_Children.end(), item); return it != m_Children.end() ? std::distance(m_Children.begin(), it) : -1; } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItem::GetChild(int index) const { return (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) ? m_Children.at(index) : 0; } void QmitkDataStorageTreeModel::TreeItem::AddChild(TreeItem *item) { this->InsertChild(item); } void QmitkDataStorageTreeModel::TreeItem::RemoveChild(TreeItem *item) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if (it != m_Children.end()) { m_Children.erase(it); item->SetParent(0); } } int QmitkDataStorageTreeModel::TreeItem::GetChildCount() const { return m_Children.size(); } int QmitkDataStorageTreeModel::TreeItem::GetIndex() const { if (m_Parent) return m_Parent->IndexOfChild(this); return 0; } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItem::GetParent() const { return m_Parent; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::TreeItem::GetDataNode() const { return m_DataNode; } void QmitkDataStorageTreeModel::TreeItem::InsertChild(TreeItem *item, int index) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if (it == m_Children.end()) { if (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) { it = m_Children.begin(); std::advance(it, index); m_Children.insert(it, item); } else m_Children.push_back(item); // add parent if necessary if (item->GetParent() != this) item->SetParent(this); } } std::vector QmitkDataStorageTreeModel::TreeItem::GetChildren() const { return m_Children; } void QmitkDataStorageTreeModel::TreeItem::SetParent(TreeItem *_Parent) { m_Parent = _Parent; if (m_Parent) m_Parent->AddChild(this); } void QmitkDataStorageTreeModel::Update() { if (!m_DataStorage.IsExpired()) { mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage.Lock()->GetAll(); /// Regardless the value of this preference, the new nodes must not be inserted /// at the top now, but at the position according to their layer. bool newNodesWereToBePlacedOnTop = m_PlaceNewNodesOnTop; m_PlaceNewNodesOnTop = false; for (const auto& node: *_NodeSet) { this->AddNodeInternal(node); } m_PlaceNewNodesOnTop = newNodesWereToBePlacedOnTop; /// Adjust the layers to ensure that derived nodes are above their sources. this->AdjustLayerProperty(); } } void QmitkDataStorageTreeModel::SetAllowHierarchyChange(bool allowHierarchyChange) { m_AllowHierarchyChange = allowHierarchyChange; } diff --git a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp index 04abdb9ffe..99ac142291 100644 --- a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp +++ b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp @@ -1,534 +1,532 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ #include "QmitkPropertiesTableModel.h" //# Own includes #include "QmitkCustomVariants.h" #include "mitkColorProperty.h" #include "mitkEnumerationProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkStringProperty.h" //# Toolkit includes #include #include #include #include //# PUBLIC CTORS,DTOR QmitkPropertiesTableModel::QmitkPropertiesTableModel(QObject *parent, mitk::PropertyList::Pointer _PropertyList) : QAbstractTableModel(parent), m_PropertyList(nullptr), m_BlockEvents(false), m_SortDescending(false), m_FilterKeyWord("") { this->SetPropertyList(_PropertyList); } QmitkPropertiesTableModel::~QmitkPropertiesTableModel() { // remove all event listeners by setting the property list to 0 this->SetPropertyList(nullptr); } //# PUBLIC GETTER mitk::PropertyList::Pointer QmitkPropertiesTableModel::GetPropertyList() const { return m_PropertyList.Lock(); } Qt::ItemFlags QmitkPropertiesTableModel::flags(const QModelIndex &index) const { // no editing so far, return default (enabled, selectable) Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.column() == PROPERTY_VALUE_COLUMN) { // there are also read only property items -> do not allow editing them if (index.data(Qt::EditRole).isValid()) flags |= Qt::ItemIsEditable; if (index.data(Qt::CheckStateRole).isValid()) flags |= Qt::ItemIsUserCheckable; } return flags; } QVariant QmitkPropertiesTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case PROPERTY_NAME_COLUMN: return tr("Name"); case PROPERTY_VALUE_COLUMN: return tr("Value"); default: return QVariant(); } } return QVariant(); } QVariant QmitkPropertiesTableModel::data(const QModelIndex &index, int role) const { // empty data by default QVariant data; if (!index.isValid() || m_SelectedProperties.empty() || index.row() > (int)(m_SelectedProperties.size() - 1)) return data; // the properties name if (index.column() == PROPERTY_NAME_COLUMN) { if (role == Qt::DisplayRole) data = QString::fromStdString(m_SelectedProperties[index.row()].first); } // the real properties value else if (index.column() == PROPERTY_VALUE_COLUMN) { mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second; if (const mitk::ColorProperty *colorProp = dynamic_cast(baseProp)) { mitk::Color col = colorProp->GetColor(); QColor qcol((int)(col.GetRed() * 255), (int)(col.GetGreen() * 255), (int)(col.GetBlue() * 255)); if (role == Qt::DisplayRole) data.setValue(qcol); else if (role == Qt::EditRole) data.setValue(qcol); } else if (mitk::BoolProperty *boolProp = dynamic_cast(baseProp)) { if (role == Qt::CheckStateRole) data = boolProp->GetValue() ? Qt::Checked : Qt::Unchecked; } else if (mitk::StringProperty *stringProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(QString::fromStdString(stringProp->GetValue())); else if (role == Qt::EditRole) data.setValue(QString::fromStdString(stringProp->GetValue())); } else if (mitk::IntProperty *intProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(intProp->GetValue()); else if (role == Qt::EditRole) data.setValue(intProp->GetValue()); } else if (mitk::FloatProperty *floatProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(floatProp->GetValue()); else if (role == Qt::EditRole) data.setValue(floatProp->GetValue()); } else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(QString::fromStdString(baseProp->GetValueAsString())); else if (role == Qt::EditRole) { QStringList values; for (auto it = enumerationProp->Begin(); it != enumerationProp->End(); it++) { values << QString::fromStdString(it->second); } data.setValue(values); } } else { if (role == Qt::DisplayRole) data.setValue(QString::fromStdString(m_SelectedProperties[index.row()].second->GetValueAsString())); } } return data; } int QmitkPropertiesTableModel::rowCount(const QModelIndex & /*parent*/) const { // return the number of properties in the properties list. return m_SelectedProperties.size(); } int QmitkPropertiesTableModel::columnCount(const QModelIndex & /*parent*/) const { return 2; } //# PUBLIC SETTER void QmitkPropertiesTableModel::SetPropertyList(mitk::PropertyList *_PropertyList) { // if propertylist really changed if (m_PropertyList != _PropertyList) { // Remove delete listener if there was a propertylist before if (!m_PropertyList.IsExpired()) - { - m_PropertyList.ObjectDelete.RemoveListener(mitk::MessageDelegate1( - this, &QmitkPropertiesTableModel::PropertyListDelete)); - } + m_PropertyList.Lock()->RemoveObserver(m_PropertyListDeleteObserverTag); // set new list m_PropertyList = _PropertyList; if (!m_PropertyList.IsExpired()) { - m_PropertyList.ObjectDelete.AddListener(mitk::MessageDelegate1( - this, &QmitkPropertiesTableModel::PropertyListDelete)); + auto command = itk::SimpleMemberCommand::New(); + command->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyListDelete); + m_PropertyListDeleteObserverTag = m_PropertyList.Lock()->AddObserver(itk::DeleteEvent(), command); } this->Reset(); } } -void QmitkPropertiesTableModel::PropertyListDelete(const itk::Object * /*_PropertyList*/) +void QmitkPropertiesTableModel::PropertyListDelete() { if (!m_BlockEvents) { m_BlockEvents = true; this->Reset(); m_BlockEvents = false; } } void QmitkPropertiesTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject & /*event*/) { if (!m_BlockEvents) { m_BlockEvents = true; int row = this->FindProperty(dynamic_cast(caller)); QModelIndex indexOfChangedProperty = index(row, 1); emit dataChanged(indexOfChangedProperty, indexOfChangedProperty); m_BlockEvents = false; } } void QmitkPropertiesTableModel::PropertyDelete(const itk::Object *caller, const itk::EventObject & /*event*/) { if (!m_BlockEvents) { m_BlockEvents = true; int row = this->FindProperty(dynamic_cast(caller)); if (row >= 0) this->Reset(); m_BlockEvents = false; } } bool QmitkPropertiesTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { // TODO: check 'role' condition if (index.isValid() && !m_SelectedProperties.empty() && index.row() < (int)(m_SelectedProperties.size()) && (role == Qt::EditRole || role == Qt::CheckStateRole)) { // block all events now! m_BlockEvents = true; auto propertyList = m_PropertyList.Lock(); // the properties name if (index.column() == PROPERTY_VALUE_COLUMN) { mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second; if (mitk::ColorProperty *colorProp = dynamic_cast(baseProp)) { QColor qcolor = value.value(); if (!qcolor.isValid()) return false; mitk::Color col = colorProp->GetColor(); col.SetRed(qcolor.red() / 255.0); col.SetGreen(qcolor.green() / 255.0); col.SetBlue(qcolor.blue() / 255.0); colorProp->SetColor(col); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::BoolProperty *boolProp = dynamic_cast(baseProp)) { boolProp->SetValue(value.toInt() == Qt::Checked ? true : false); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::StringProperty *stringProp = dynamic_cast(baseProp)) { stringProp->SetValue((value.value()).toStdString()); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::IntProperty *intProp = dynamic_cast(baseProp)) { int intValue = value.value(); if (intValue != intProp->GetValue()) { intProp->SetValue(intValue); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } else if (mitk::FloatProperty *floatProp = dynamic_cast(baseProp)) { float floatValue = value.value(); if (floatValue != floatProp->GetValue()) { floatProp->SetValue(floatValue); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast(baseProp)) { std::string activatedItem = value.value().toStdString(); if (activatedItem != enumerationProp->GetValueAsString()) { if (enumerationProp->IsValidEnumerationValue(activatedItem)) { enumerationProp->SetValue(activatedItem); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } } // property was changed by us, now we can accept property changes triggered by someone else m_BlockEvents = false; emit dataChanged(index, index); return true; } return false; } void QmitkPropertiesTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */) { bool sortDescending = (order == Qt::DescendingOrder) ? true : false; // do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!) if (sortDescending != m_SortDescending) { m_SortDescending = sortDescending; PropertyDataSetCompareFunction::CompareCriteria _CompareCriteria = PropertyDataSetCompareFunction::CompareByName; PropertyDataSetCompareFunction::CompareOperator _CompareOperator = m_SortDescending ? PropertyDataSetCompareFunction::Greater : PropertyDataSetCompareFunction::Less; if (column == PROPERTY_VALUE_COLUMN) _CompareCriteria = PropertyDataSetCompareFunction::CompareByValue; PropertyDataSetCompareFunction compareFunc(_CompareCriteria, _CompareOperator); std::sort(m_SelectedProperties.begin(), m_SelectedProperties.end(), compareFunc); QAbstractTableModel::beginResetModel(); QAbstractTableModel::endResetModel(); } } //# PROTECTED GETTER int QmitkPropertiesTableModel::FindProperty(const mitk::BaseProperty *_Property) const { int row = -1; if (_Property) { // search for property that changed and emit datachanged on the corresponding ModelIndex std::vector::const_iterator propertyIterator; for (propertyIterator = m_SelectedProperties.begin(); propertyIterator != m_SelectedProperties.end(); propertyIterator++) { if (propertyIterator->second == _Property) break; } if (propertyIterator != m_SelectedProperties.end()) row = std::distance(m_SelectedProperties.begin(), propertyIterator); } return row; } //# PROTECTED SETTER void QmitkPropertiesTableModel::AddSelectedProperty(PropertyDataSet &_PropertyDataSet) { // subscribe for modified event itk::MemberCommand::Pointer _PropertyDataSetModifiedCommand = itk::MemberCommand::New(); _PropertyDataSetModifiedCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyModified); m_PropertyModifiedObserverTags.push_back( _PropertyDataSet.second->AddObserver(itk::ModifiedEvent(), _PropertyDataSetModifiedCommand)); // subscribe for delete event itk::MemberCommand::Pointer _PropertyDataSetDeleteCommand = itk::MemberCommand::New(); _PropertyDataSetDeleteCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyDelete); m_PropertyDeleteObserverTags.push_back( _PropertyDataSet.second->AddObserver(itk::DeleteEvent(), _PropertyDataSetDeleteCommand)); // add to the selection m_SelectedProperties.push_back(_PropertyDataSet); } void QmitkPropertiesTableModel::RemoveSelectedProperty(unsigned int _Index) { PropertyDataSet &_PropertyDataSet = m_SelectedProperties.at(_Index); // remove modified event listener _PropertyDataSet.second->RemoveObserver(m_PropertyModifiedObserverTags[_Index]); m_PropertyModifiedObserverTags.erase(m_PropertyModifiedObserverTags.begin() + _Index); // remove delete event listener _PropertyDataSet.second->RemoveObserver(m_PropertyDeleteObserverTags[_Index]); m_PropertyDeleteObserverTags.erase(m_PropertyDeleteObserverTags.begin() + _Index); // remove from selection m_SelectedProperties.erase(m_SelectedProperties.begin() + _Index); } void QmitkPropertiesTableModel::Reset() { // remove all selected properties while (!m_SelectedProperties.empty()) { this->RemoveSelectedProperty(m_SelectedProperties.size() - 1); } std::vector allPredicates; if (!m_PropertyList.IsExpired()) { auto propertyList = m_PropertyList.Lock(); // first of all: collect all properties from the list for (auto it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); it++) { allPredicates.push_back(*it); //% TODO } } // make a subselection if a keyword is specified if (!m_FilterKeyWord.empty()) { std::vector subSelection; for (auto it = allPredicates.begin(); it != allPredicates.end(); it++) { // add this to the selection if it is matched by the keyword if ((*it).first.find(m_FilterKeyWord) != std::string::npos) subSelection.push_back((*it)); } allPredicates.clear(); allPredicates = subSelection; } PropertyDataSet tmpPropertyDataSet; // add all selected now to the Model for (auto it = allPredicates.begin(); it != allPredicates.end(); it++) { tmpPropertyDataSet = *it; this->AddSelectedProperty(tmpPropertyDataSet); } // sort the list as indicated by m_SortDescending this->sort(m_SortDescending); // model was resetted QAbstractTableModel::beginResetModel(); QAbstractTableModel::endResetModel(); } void QmitkPropertiesTableModel::SetFilterPropertiesKeyWord(std::string _FilterKeyWord) { m_FilterKeyWord = _FilterKeyWord; this->Reset(); } QmitkPropertiesTableModel::PropertyDataSetCompareFunction::PropertyDataSetCompareFunction( CompareCriteria _CompareCriteria, CompareOperator _CompareOperator) : m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator) { } bool QmitkPropertiesTableModel::PropertyDataSetCompareFunction::operator()(const PropertyDataSet &_Left, const PropertyDataSet &_Right) const { switch (m_CompareCriteria) { case CompareByValue: if (m_CompareOperator == Less) return (_Left.second->GetValueAsString() < _Right.second->GetValueAsString()); else return (_Left.second->GetValueAsString() > _Right.second->GetValueAsString()); break; // CompareByName: default: if (m_CompareOperator == Less) return (_Left.first < _Right.first); else return (_Left.first > _Right.first); break; } } QmitkPropertiesTableModel::PropertyListElementFilterFunction::PropertyListElementFilterFunction( const std::string &_FilterKeyWord) : m_FilterKeyWord(_FilterKeyWord) { } bool QmitkPropertiesTableModel::PropertyListElementFilterFunction::operator()(const PropertyDataSet &_Elem) const { if (m_FilterKeyWord.empty()) return true; return (_Elem.first.find(m_FilterKeyWord) == 0); } diff --git a/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp b/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp index 54a6209434..c6d0d4656c 100644 --- a/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp +++ b/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp @@ -1,541 +1,518 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ #include "QmitkPropertyItemModel.h" #include "QmitkPropertyItem.h" #include #include #include #include #include #include #include #include #include #include #include template T *GetPropertyService() { us::ModuleContext *context = us::GetModuleContext(); us::ServiceReference serviceRef = context->GetServiceReference(); return serviceRef ? context->GetService(serviceRef) : nullptr; } static QColor MitkToQt(const mitk::Color &color) { return QColor(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); } static mitk::BaseProperty *GetBaseProperty(const QVariant &data) { return data.isValid() ? reinterpret_cast(data.value()) : nullptr; } static mitk::Color QtToMitk(const QColor &color) { mitk::Color mitkColor; mitkColor.SetRed(color.red() / 255.0f); mitkColor.SetGreen(color.green() / 255.0f); mitkColor.SetBlue(color.blue() / 255.0f); return mitkColor; } class PropertyEqualTo { public: PropertyEqualTo(const mitk::BaseProperty *property) : m_Property(property) {} bool operator()(const mitk::PropertyList::PropertyMapElementType &pair) const { return pair.second.GetPointer() == m_Property; } private: const mitk::BaseProperty *m_Property; }; QmitkPropertyItemModel::QmitkPropertyItemModel(QObject *parent) : QAbstractItemModel(parent), m_ShowAliases(false), m_FilterProperties(false), m_PropertyAliases(nullptr), m_PropertyFilters(nullptr) { this->CreateRootItem(); } QmitkPropertyItemModel::~QmitkPropertyItemModel() { this->SetNewPropertyList(nullptr); } int QmitkPropertyItemModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return static_cast(parent.internalPointer())->GetColumnCount(); else return m_RootItem->GetColumnCount(); } void QmitkPropertyItemModel::CreateRootItem() { QList rootData; rootData << "Property" << "Value"; m_RootItem.reset(new QmitkPropertyItem(rootData)); this->beginResetModel(); this->endResetModel(); } QVariant QmitkPropertyItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); mitk::BaseProperty *property = index.column() == 1 ? GetBaseProperty(static_cast(index.internalPointer())->GetData(1)) : nullptr; if (role == Qt::DisplayRole) { if (index.column() == 0) { return static_cast(index.internalPointer())->GetData(0); } else if (index.column() == 1 && property != nullptr) { if (auto colorProperty = dynamic_cast(property)) return MitkToQt(colorProperty->GetValue()); else if (dynamic_cast(property) == nullptr) return QString::fromStdString(property->GetValueAsString()); } } else if (index.column() == 1 && property != nullptr) { if (role == Qt::CheckStateRole) { if (auto boolProperty = dynamic_cast(property)) return boolProperty->GetValue() ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::EditRole) { if (dynamic_cast(property) != nullptr) { return QString::fromStdString(property->GetValueAsString()); } else if (auto intProperty = dynamic_cast(property)) { return intProperty->GetValue(); } else if (auto floatProperty = dynamic_cast(property)) { return floatProperty->GetValue(); } else if (auto doubleProperty = dynamic_cast(property)) { return doubleProperty->GetValue(); } else if (auto enumProperty = dynamic_cast(property)) { QStringList values; for (mitk::EnumerationProperty::EnumConstIterator it = enumProperty->Begin(); it != enumProperty->End(); it++) values << QString::fromStdString(it->second); return values; } else if (auto colorProperty = dynamic_cast(property)) { return MitkToQt(colorProperty->GetValue()); } } else if (role == mitk::PropertyRole) { return QVariant::fromValue(property); } } return QVariant(); } QModelIndex QmitkPropertyItemModel::FindProperty(const mitk::BaseProperty *property) { if (property == nullptr) return QModelIndex(); auto propertyMap = m_PropertyList.Lock()->GetMap(); auto it = std::find_if(propertyMap->begin(), propertyMap->end(), PropertyEqualTo(property)); if (it == propertyMap->end()) return QModelIndex(); QString name = QString::fromStdString(it->first); if (!name.contains('.')) { QModelIndexList item = this->match(index(0, 0), Qt::DisplayRole, name, 1, Qt::MatchExactly); if (!item.empty()) return item[0]; } else { QStringList names = name.split('.'); QModelIndexList items = this->match(index(0, 0), Qt::DisplayRole, names.last(), -1, Qt::MatchRecursive | Qt::MatchExactly); for (auto item : items) { QModelIndex candidate = item; for (int i = names.length() - 1; i != 0; --i) { QModelIndex parent = item.parent(); if (parent.parent() == QModelIndex()) { if (parent.data() != names.first()) break; return candidate; } if (parent.data() != names[i - 1]) break; item = parent; } } } return QModelIndex(); } Qt::ItemFlags QmitkPropertyItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.column() == 1) { if (index.data(Qt::EditRole).isValid()) flags |= Qt::ItemIsEditable; if (index.data(Qt::CheckStateRole).isValid()) flags |= Qt::ItemIsUserCheckable; } return flags; } mitk::PropertyList *QmitkPropertyItemModel::GetPropertyList() const { return m_PropertyList.Lock().GetPointer(); } QVariant QmitkPropertyItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_RootItem->GetData(section); return QVariant(); } QModelIndex QmitkPropertyItemModel::index(int row, int column, const QModelIndex &parent) const { if (!this->hasIndex(row, column, parent)) return QModelIndex(); QmitkPropertyItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_RootItem.get(); QmitkPropertyItem *childItem = parentItem->GetChild(row); return childItem != nullptr ? this->createIndex(row, column, childItem) : QModelIndex(); } void QmitkPropertyItemModel::OnPreferencesChanged() { bool updateAliases = m_ShowAliases != (m_PropertyAliases != nullptr); bool updateFilters = m_FilterProperties != (m_PropertyFilters != nullptr); bool resetPropertyList = false; if (updateAliases) { m_PropertyAliases = m_ShowAliases ? GetPropertyService() : nullptr; resetPropertyList = !m_PropertyList.IsExpired(); } if (updateFilters) { m_PropertyFilters = m_FilterProperties ? GetPropertyService() : nullptr; if (!resetPropertyList) resetPropertyList = !m_PropertyList.IsExpired(); } if (resetPropertyList) this->SetNewPropertyList(m_PropertyList.Lock()); } -void QmitkPropertyItemModel::OnPropertyDeleted(const itk::Object * /*property*/, const itk::EventObject &) +void QmitkPropertyItemModel::OnPropertyListModified() { - /*QModelIndex index = this->FindProperty(static_cast(property)); - - if (index != QModelIndex()) - this->reset();*/ -} - -void QmitkPropertyItemModel::OnPropertyListModified(const itk::Object *propertyList) -{ - this->SetNewPropertyList(dynamic_cast(const_cast(propertyList))); + this->SetNewPropertyList(m_PropertyList.Lock()); } -void QmitkPropertyItemModel::OnPropertyListDeleted(const itk::Object *) +void QmitkPropertyItemModel::OnPropertyListDeleted() { this->CreateRootItem(); } void QmitkPropertyItemModel::OnPropertyModified(const itk::Object *property, const itk::EventObject &) { QModelIndex index = this->FindProperty(static_cast(property)); if (index != QModelIndex()) emit dataChanged(index, index); } QModelIndex QmitkPropertyItemModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); QmitkPropertyItem *parentItem = static_cast(child.internalPointer())->GetParent(); if (parentItem == m_RootItem.get()) return QModelIndex(); return this->createIndex(parentItem->GetRow(), 0, parentItem); } int QmitkPropertyItemModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; QmitkPropertyItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_RootItem.get(); return parentItem->GetChildCount(); } bool QmitkPropertyItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.column() != 1 || (role != Qt::EditRole && role != Qt::CheckStateRole)) return false; mitk::BaseProperty *property = GetBaseProperty(static_cast(index.internalPointer())->GetData(1)); if (property == nullptr) return false; if (mitk::BoolProperty *boolProperty = dynamic_cast(property)) { boolProperty->SetValue(value.toInt() == Qt::Checked ? true : false); } else if (mitk::StringProperty *stringProperty = dynamic_cast(property)) { stringProperty->SetValue(value.toString().toStdString()); } else if (mitk::IntProperty *intProperty = dynamic_cast(property)) { intProperty->SetValue(value.toInt()); } else if (mitk::FloatProperty *floatProperty = dynamic_cast(property)) { floatProperty->SetValue(value.toFloat()); } else if (mitk::DoubleProperty *doubleProperty = dynamic_cast(property)) { doubleProperty->SetValue(value.toDouble()); } else if (mitk::EnumerationProperty *enumProperty = dynamic_cast(property)) { std::string selection = value.toString().toStdString(); if (selection != enumProperty->GetValueAsString() && enumProperty->IsValidEnumerationValue(selection)) enumProperty->SetValue(selection); } else if (mitk::ColorProperty *colorProperty = dynamic_cast(property)) { colorProperty->SetValue(QtToMitk(value.value())); } auto propertyList = m_PropertyList.Lock(); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } -void QmitkPropertyItemModel::SetNewPropertyList(mitk::PropertyList *propertyList) +void QmitkPropertyItemModel::SetNewPropertyList(mitk::PropertyList *newPropertyList) { typedef mitk::PropertyList::PropertyMap PropertyMap; this->beginResetModel(); if (!m_PropertyList.IsExpired()) { - mitk::MessageDelegate1 onPropertyListDeleted( - this, &QmitkPropertyItemModel::OnPropertyListDeleted); - m_PropertyList.ObjectDelete.RemoveListener(onPropertyListDeleted); + auto propertyList = m_PropertyList.Lock(); - mitk::MessageDelegate1 onPropertyListModified( - this, &QmitkPropertyItemModel::OnPropertyListModified); - m_PropertyList.ObjectModified.RemoveListener(onPropertyListModified); + propertyList->RemoveObserver(m_PropertyListDeletedTag); + propertyList->RemoveObserver(m_PropertyListModifiedTag); const PropertyMap *propertyMap = m_PropertyList.Lock()->GetMap(); for (PropertyMap::const_iterator propertyIt = propertyMap->begin(); propertyIt != propertyMap->end(); ++propertyIt) { std::map::const_iterator tagIt = m_PropertyModifiedTags.find(propertyIt->first); if (tagIt != m_PropertyModifiedTags.end()) propertyIt->second->RemoveObserver(tagIt->second); tagIt = m_PropertyDeletedTags.find(propertyIt->first); if (tagIt != m_PropertyDeletedTags.end()) propertyIt->second->RemoveObserver(tagIt->second); } m_PropertyModifiedTags.clear(); m_PropertyDeletedTags.clear(); } - m_PropertyList = propertyList; + m_PropertyList = newPropertyList; if (!m_PropertyList.IsExpired()) { - mitk::MessageDelegate1 onPropertyListModified( - this, &QmitkPropertyItemModel::OnPropertyListModified); - m_PropertyList.ObjectModified.AddListener(onPropertyListModified); - - mitk::MessageDelegate1 onPropertyListDeleted( - this, &QmitkPropertyItemModel::OnPropertyListDeleted); - m_PropertyList.ObjectDelete.AddListener(onPropertyListDeleted); + auto onPropertyListModified = itk::SimpleMemberCommand::New(); + onPropertyListModified->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyListModified); + m_PropertyListModifiedTag = m_PropertyList.Lock()->AddObserver(itk::ModifiedEvent(), onPropertyListModified); - mitk::MessageDelegate2 onPropertyModified( - this, &QmitkPropertyItemModel::OnPropertyModified); + auto onPropertyListDeleted = itk::SimpleMemberCommand::New(); + onPropertyListDeleted->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyListDeleted); + m_PropertyListDeletedTag = m_PropertyList.Lock()->AddObserver(itk::DeleteEvent(), onPropertyListDeleted); - itk::MemberCommand::Pointer modifiedCommand = - itk::MemberCommand::New(); + auto modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyModified); const PropertyMap *propertyMap = m_PropertyList.Lock()->GetMap(); for (PropertyMap::const_iterator it = propertyMap->begin(); it != propertyMap->end(); ++it) m_PropertyModifiedTags.insert( std::make_pair(it->first, it->second->AddObserver(itk::ModifiedEvent(), modifiedCommand))); - - itk::MemberCommand::Pointer deletedCommand = - itk::MemberCommand::New(); - deletedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyDeleted); - - for (PropertyMap::const_iterator it = propertyMap->begin(); it != propertyMap->end(); ++it) - m_PropertyDeletedTags.insert( - std::make_pair(it->first, it->second->AddObserver(itk::DeleteEvent(), deletedCommand))); } this->CreateRootItem(); if (m_PropertyList != nullptr && !m_PropertyList.Lock()->IsEmpty()) { mitk::PropertyList::PropertyMap filteredProperties; bool filterProperties = false; if (m_PropertyFilters != nullptr && (m_PropertyFilters->HasFilter() || m_PropertyFilters->HasFilter(m_ClassName.toStdString()))) { filteredProperties = m_PropertyFilters->ApplyFilter(*m_PropertyList.Lock()->GetMap(), m_ClassName.toStdString()); filterProperties = true; } const mitk::PropertyList::PropertyMap *propertyMap = !filterProperties ? m_PropertyList.Lock()->GetMap() : &filteredProperties; mitk::PropertyList::PropertyMap::const_iterator end = propertyMap->end(); for (mitk::PropertyList::PropertyMap::const_iterator iter = propertyMap->begin(); iter != end; ++iter) { std::vector aliases; if (m_PropertyAliases != nullptr) { aliases = m_PropertyAliases->GetAliases(iter->first, m_ClassName.toStdString()); if (aliases.empty() && !m_ClassName.isEmpty()) aliases = m_PropertyAliases->GetAliases(iter->first); } if (aliases.empty()) { QList data; data << QString::fromStdString(iter->first) << QVariant::fromValue((reinterpret_cast(iter->second.GetPointer()))); m_RootItem->AppendChild(new QmitkPropertyItem(data)); } else { std::vector::const_iterator end = aliases.end(); for (std::vector::const_iterator aliasIter = aliases.begin(); aliasIter != end; ++aliasIter) { QList data; data << QString::fromStdString(*aliasIter) << QVariant::fromValue((reinterpret_cast(iter->second.GetPointer()))); m_RootItem->AppendChild(new QmitkPropertyItem(data)); } } } } this->endResetModel(); } void QmitkPropertyItemModel::SetPropertyList(mitk::PropertyList *propertyList, const QString &className) { if (m_PropertyList != propertyList) { m_ClassName = className; this->SetNewPropertyList(propertyList); } } void QmitkPropertyItemModel::Update() { this->SetNewPropertyList(m_PropertyList.Lock()); } diff --git a/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h b/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h index ce923201d5..d4d46978c3 100644 --- a/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h +++ b/Modules/QtWidgetsExt/include/QmitkPointListViewWidget.h @@ -1,100 +1,104 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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 QmitkPointListViewWidget_h #define QmitkPointListViewWidget_h #include "MitkQtWidgetsExtExports.h" #include #include #include class QmitkStdMultiWidget; /*! * \brief GUI widget for handling mitk::PointSet * * Displays all the points in a mitk::PointSet graphically. * Reacts automatically to changes in the PointSet's selection status. * Updates PointSet's selection status when this list's selection changes. * * If a QmitkStdMultiWidget is assigned via SetMultiWidget(), the * crosshair of the QmitkStdMultiWidget is moved to the currently selected * point. * */ class MITKQTWIDGETSEXT_EXPORT QmitkPointListViewWidget : public QListWidget { Q_OBJECT signals: void PointSelectionChanged(); ///< this signal is emmitted, if the selection of a point in the pointset is changed public: QmitkPointListViewWidget(QWidget *parent = 0); ~QmitkPointListViewWidget() override; /// assign a point set for observation void SetPointSet(mitk::PointSet *pointSet); /// which point set to work on const mitk::PointSet *GetPointSet() const; void SetMultiWidget( QmitkStdMultiWidget *multiWidget); ///< assign a QmitkStdMultiWidget for updating render window crosshair QmitkStdMultiWidget *GetMultiWidget() const; ///< return the QmitkStdMultiWidget that is used for updating render window crosshair /// which time step to display/model void SetTimeStep(int t); /// which time step to display/model int GetTimeStep() const; /// observer for point set "modified" events - void OnPointSetChanged(const itk::Object * /*obj*/); + void OnPointSetChanged(); /// observer for point set "delete" events - void OnPointSetDeleted(const itk::Object * /*obj*/); + void OnPointSetDeleted(); protected slots: /// /// Filtering double click event for editing point coordinates via a dialog /// void OnItemDoubleClicked(QListWidgetItem *item); /// called when the selection of the view widget changes void OnCurrentRowChanged(int /*currentRow*/); protected: void keyPressEvent(QKeyEvent *e) override; ///< react to F2, F3 and DEL keys void MoveSelectedPointUp(); void MoveSelectedPointDown(); void RemoveSelectedPoint(); void Update(bool currentRowChanged = false); protected: mitk::WeakPointer m_PointSet; + + unsigned long m_PointSetDeletedTag; + unsigned long m_PointSetModifiedTag; + int m_TimeStep; bool m_SelfCall; /// used to position the planes on a selected point QmitkStdMultiWidget *m_MultiWidget; }; #endif diff --git a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp index acc9ab0fee..dec31d5cba 100644 --- a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp +++ b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp @@ -1,255 +1,260 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. 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. ===================================================================*/ #include "QmitkPointListViewWidget.h" #include "QmitkEditPointDialog.h" #include "QmitkPointListModel.h" #include "QmitkStdMultiWidget.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkRenderingManager.h" #include QmitkPointListViewWidget::QmitkPointListViewWidget(QWidget *parent) : QListWidget(parent), m_TimeStep(0), m_SelfCall(false), m_MultiWidget(nullptr) { QListWidget::setAlternatingRowColors(true); // logic QListWidget::setSelectionBehavior(QAbstractItemView::SelectRows); QListWidget::setSelectionMode(QAbstractItemView::SingleSelection); connect(this, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(OnItemDoubleClicked(QListWidgetItem *))); connect(this, SIGNAL(currentRowChanged(int)), this, SLOT(OnCurrentRowChanged(int))); } QmitkPointListViewWidget::~QmitkPointListViewWidget() { this->SetPointSet(0); // remove listener } void QmitkPointListViewWidget::SetPointSet(mitk::PointSet *pointSet) { if (!m_PointSet.IsExpired()) { - m_PointSet.ObjectModified.RemoveListener(mitk::MessageDelegate1( - this, &QmitkPointListViewWidget::OnPointSetChanged)); - m_PointSet.ObjectDelete.RemoveListener(mitk::MessageDelegate1( - this, &QmitkPointListViewWidget::OnPointSetDeleted)); + auto pointSet = m_PointSet.Lock(); + + pointSet->RemoveObserver(m_PointSetModifiedTag); + pointSet->RemoveObserver(m_PointSetDeletedTag); } m_PointSet = pointSet; if (!m_PointSet.IsExpired()) { - m_PointSet.ObjectModified.AddListener(mitk::MessageDelegate1( - this, &QmitkPointListViewWidget::OnPointSetChanged)); - m_PointSet.ObjectDelete.AddListener(mitk::MessageDelegate1( - this, &QmitkPointListViewWidget::OnPointSetDeleted)); + auto pointSet = m_PointSet.Lock(); + + auto onPointSetDeleted = itk::SimpleMemberCommand::New(); + onPointSetDeleted->SetCallbackFunction(this, &QmitkPointListViewWidget::OnPointSetDeleted); + m_PointSetDeletedTag = pointSet->AddObserver(itk::DeleteEvent(), onPointSetDeleted); + + auto onPointSetModified = itk::SimpleMemberCommand::New(); + onPointSetModified->SetCallbackFunction(this, &QmitkPointListViewWidget::OnPointSetChanged); + m_PointSetModifiedTag = pointSet->AddObserver(itk::DeleteEvent(), onPointSetModified); } this->Update(); } const mitk::PointSet *QmitkPointListViewWidget::GetPointSet() const { return m_PointSet.Lock(); } void QmitkPointListViewWidget::SetTimeStep(int t) { m_TimeStep = t; this->Update(); } int QmitkPointListViewWidget::GetTimeStep() const { return m_TimeStep; } void QmitkPointListViewWidget::SetMultiWidget(QmitkStdMultiWidget *multiWidget) { m_MultiWidget = multiWidget; } QmitkStdMultiWidget *QmitkPointListViewWidget::GetMultiWidget() const { return m_MultiWidget; } -void QmitkPointListViewWidget::OnPointSetChanged(const itk::Object *) +void QmitkPointListViewWidget::OnPointSetChanged() { if (!m_SelfCall) this->Update(); } -void QmitkPointListViewWidget::OnPointSetDeleted(const itk::Object *) +void QmitkPointListViewWidget::OnPointSetDeleted() { this->SetPointSet(0); this->Update(); } void QmitkPointListViewWidget::OnItemDoubleClicked(QListWidgetItem *item) { QmitkEditPointDialog _EditPointDialog(this); _EditPointDialog.SetPoint(m_PointSet.Lock(), this->row(item), m_TimeStep); _EditPointDialog.exec(); } void QmitkPointListViewWidget::OnCurrentRowChanged(int) { this->Update(true); } void QmitkPointListViewWidget::keyPressEvent(QKeyEvent *e) { if (m_PointSet.IsExpired()) return; int key = e->key(); switch (key) { case Qt::Key_F2: this->MoveSelectedPointUp(); break; case Qt::Key_F3: this->MoveSelectedPointDown(); break; case Qt::Key_Delete: this->RemoveSelectedPoint(); break; default: break; } } void QmitkPointListViewWidget::MoveSelectedPointUp() { if (m_PointSet.IsExpired()) return; auto pointSet = m_PointSet.Lock(); mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListViewWidget::MoveSelectedPointDown() { if (m_PointSet.IsExpired()) return; auto pointSet = m_PointSet.Lock(); mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListViewWidget::RemoveSelectedPoint() { if (m_PointSet.IsExpired()) return; auto pointSet = m_PointSet.Lock(); mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = new mitk::PointOperation(mitk::OpREMOVE, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListViewWidget::Update(bool currentRowChanged) { if (m_SelfCall) return; if (m_PointSet.IsExpired()) { this->clear(); return; } auto pointSet = m_PointSet.Lock(); m_SelfCall = true; QString text; int i = 0; mitk::PointSet::DataType::Pointer pointset = pointSet->GetPointSet(m_TimeStep); for (mitk::PointSet::PointsContainer::Iterator it = pointset->GetPoints()->Begin(); it != pointset->GetPoints()->End(); ++it) { text = QString("%0: (%1, %2, %3)") .arg(i, 3) .arg(it.Value().GetElement(0), 0, 'f', 3) .arg(it.Value().GetElement(1), 0, 'f', 3) .arg(it.Value().GetElement(2), 0, 'f', 3); if (i == this->count()) this->addItem(text); // insert text else this->item(i)->setText(text); // update text if (currentRowChanged) { if (i == this->currentRow()) pointSet->SetSelectInfo(this->currentRow(), true, m_TimeStep); else pointSet->SetSelectInfo(it->Index(), false, m_TimeStep); // select nothing now } ++i; } // remove unnecessary listwidgetitems while (pointSet->GetPointSet(m_TimeStep)->GetPoints()->Size() < (unsigned int)this->count()) { QListWidgetItem *item = this->takeItem(this->count() - 1); delete item; } // update selection in pointset or in the list widget if (!currentRowChanged) { if (pointSet->GetNumberOfSelected(m_TimeStep) > 1) { /// @TODO use logging as soon as available std::cerr << "Point set has multiple selected points. This view is not designed for more than one selected point." << std::endl; } int selectedIndex = pointSet->SearchSelectedPoint(m_TimeStep); if (selectedIndex != -1) // no selected point is found { this->setCurrentRow(selectedIndex); } } m_SelfCall = false; }