//################################################################################################## // // Custom Visualization Core library // // This library may be used under the terms of either the GNU General Public License or // the GNU Lesser General Public License as follows: // // GNU General Public License Usage // This library is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at <> // for more details. // // GNU Lesser General Public License Usage // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU Lesser General Public License at <> // for more details. // //################################################################################################## #include "cafPdmUiTreeSelectionQModel.h" #include "cafPdmObject.h" #include "cafPdmUiCommandSystemProxy.h" #include "cafPdmUiTreeViewQModel.h" #include #include #include #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmUiTreeSelectionQModel::PdmUiTreeSelectionQModel( QObject* parent /*= 0*/ ) : QAbstractItemModel( parent ) , m_uiFieldHandle( nullptr ) , m_uiValueCache( nullptr ) , m_tree( nullptr ) , m_singleSelectionMode( false ) , m_indexForLastUncheckedItem( QModelIndex() ) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmUiTreeSelectionQModel::~PdmUiTreeSelectionQModel() { m_uiFieldHandle = nullptr; delete m_tree; m_tree = nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int caf::PdmUiTreeSelectionQModel::headingRole() { return Qt::UserRole + 1; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int caf::PdmUiTreeSelectionQModel::optionItemValueRole() { return Qt::UserRole + 2; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::setCheckedStateForItems( const QModelIndexList& sourceModelIndices, bool checked ) { if ( !m_uiFieldHandle || !m_uiFieldHandle->uiField() ) return; std::set selectedIndices; { QVariant fieldValue = m_uiFieldHandle->uiField()->uiValue(); QList fieldValueSelection = fieldValue.toList(); for ( auto v : fieldValueSelection ) { selectedIndices.insert( v.toUInt() ); } } if ( checked ) { for ( auto mi : sourceModelIndices ) { const caf::PdmOptionItemInfo* optionItemInfo = optionItem( mi ); if ( !optionItemInfo->isReadOnly() ) { selectedIndices.insert( static_cast( optionIndex( mi ) ) ); } } } else { for ( auto mi : sourceModelIndices ) { const caf::PdmOptionItemInfo* optionItemInfo = optionItem( mi ); if ( !optionItemInfo->isReadOnly() ) { selectedIndices.erase( static_cast( optionIndex( mi ) ) ); } } } QList fieldValueSelection; for ( auto v : selectedIndices ) { fieldValueSelection.push_back( QVariant( v ) ); } PdmUiCommandSystemProxy::instance()->setUiValueToField( m_uiFieldHandle->uiField(), fieldValueSelection ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::enableSingleSelectionMode( bool enable ) { m_singleSelectionMode = enable; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int caf::PdmUiTreeSelectionQModel::optionItemCount() const { return m_options.size(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::setOptions( caf::PdmUiFieldEditorHandle* field, const QList& options ) { m_uiFieldHandle = field; bool mustRebuildOptionItemTree = m_options.size() != options.size(); m_options = options; if ( mustRebuildOptionItemTree ) { beginResetModel(); if ( m_tree ) { delete m_tree; m_tree = nullptr; } m_tree = new TreeItemType( nullptr, -1, 0 ); buildOptionItemTree( 0, m_tree ); endResetModel(); } else { // Notify changed for all items in the model as UI can change even if the option item count is identical // It is possible to use beginResetModel and endResetModel, but this will also invalidate tree expand state notifyChangedForAllModelIndices(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::setUiValueCache( const QVariant* uiValuesCache ) { m_uiValueCache = uiValuesCache; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::resetUiValueCache() { m_uiValueCache = nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool caf::PdmUiTreeSelectionQModel::isReadOnly( const QModelIndex& index ) const { return optionItem( index )->isReadOnly(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool caf::PdmUiTreeSelectionQModel::isChecked( const QModelIndex& index ) const { return data( index, Qt::CheckStateRole ).toBool(); } //-------------------------------------------------------------------------------------------------- /// Checks if this is a real tree with grand children or just a list of children. //-------------------------------------------------------------------------------------------------- bool caf::PdmUiTreeSelectionQModel::hasGrandChildren() const { return m_tree && m_tree->hasGrandChildren(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const caf::PdmOptionItemInfo* caf::PdmUiTreeSelectionQModel::optionItem( const QModelIndex& index ) const { int opIndex = optionIndex( index ); return &m_options[opIndex]; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int caf::PdmUiTreeSelectionQModel::optionIndex( const QModelIndex& index ) const { CAF_ASSERT( index.isValid() ); TreeItemType* item = static_cast( index.internalPointer() ); int optionIndex = item->dataObject(); return optionIndex; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- Qt::ItemFlags caf::PdmUiTreeSelectionQModel::flags( const QModelIndex& index ) const { if ( index.isValid() ) { const caf::PdmOptionItemInfo* optionItemInfo = optionItem( index ); if ( !optionItemInfo->isHeading() ) { if ( optionItemInfo->isReadOnly() ) { return QAbstractItemModel::flags( index ) ^ Qt::ItemIsEnabled; } return QAbstractItemModel::flags( index ) | Qt::ItemIsUserCheckable; } } return QAbstractItemModel::flags( index ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QModelIndex caf::PdmUiTreeSelectionQModel::index( int row, int column, const QModelIndex& parent /*= QModelIndex()*/ ) const { if ( !hasIndex( row, column, parent ) ) return QModelIndex(); TreeItemType* parentItem; if ( !parent.isValid() ) parentItem = m_tree; else parentItem = static_cast( parent.internalPointer() ); TreeItemType* childItem = parentItem->child( row ); if ( childItem ) return createIndex( row, column, childItem ); else return QModelIndex(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int caf::PdmUiTreeSelectionQModel::columnCount( const QModelIndex& parent /*= QModelIndex()*/ ) const { return 1; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QModelIndex caf::PdmUiTreeSelectionQModel::parent( const QModelIndex& index ) const { if ( !index.isValid() ) return QModelIndex(); TreeItemType* childItem = static_cast( index.internalPointer() ); TreeItemType* parentItem = childItem->parent(); if ( parentItem == m_tree ) return QModelIndex(); return createIndex( parentItem->row(), 0, parentItem ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int caf::PdmUiTreeSelectionQModel::rowCount( const QModelIndex& parent /*= QModelIndex()*/ ) const { if ( !m_tree ) return 0; if ( parent.column() > 0 ) return 0; TreeItemType* parentItem; if ( !parent.isValid() ) parentItem = m_tree; else parentItem = static_cast( parent.internalPointer() ); return parentItem->childCount(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QVariant caf::PdmUiTreeSelectionQModel::data( const QModelIndex& index, int role /*= Qt::DisplayRole*/ ) const { if ( index.isValid() ) { const caf::PdmOptionItemInfo* optionItemInfo = optionItem( index ); if ( role == Qt::DisplayRole ) { return optionItemInfo->optionUiText(); } else if ( role == Qt::DecorationRole ) { auto icon = optionItemInfo->icon(); return icon ? *icon : QIcon(); } else if ( role == Qt::CheckStateRole && !optionItemInfo->isHeading() ) { if ( m_uiFieldHandle && m_uiFieldHandle->uiField() ) { // Avoid calling the seriously heavy uiValue method if we have a temporary valid cache. QVariant fieldValue = m_uiValueCache ? *m_uiValueCache : m_uiFieldHandle->uiField()->uiValue(); if ( isSingleValueField( fieldValue ) ) { int row = fieldValue.toInt(); if ( row == optionIndex( index ) ) { return Qt::Checked; } } else if ( isMultipleValueField( fieldValue ) ) { QList valuesSelectedInField = fieldValue.toList(); int opIndex = optionIndex( index ); for ( QVariant v : valuesSelectedInField ) { int indexInField = v.toInt(); if ( indexInField == opIndex ) { return Qt::Checked; } } } } return Qt::Unchecked; } else if ( role == Qt::FontRole ) { if ( optionItemInfo->isHeading() ) { QFont font; font.setBold( true ); return font; } } else if ( role == headingRole() ) { return optionItemInfo->isHeading(); } else if ( role == optionItemValueRole() ) { QVariant v = optionItemInfo->value(); return v; } } return QVariant(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool caf::PdmUiTreeSelectionQModel::setData( const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/ ) { if ( !m_uiFieldHandle || !m_uiFieldHandle->uiField() ) return false; if ( role == Qt::CheckStateRole ) { QVariant fieldValue = m_uiFieldHandle->uiField()->uiValue(); if ( isSingleValueField( fieldValue ) ) { if ( value.toBool() == true ) { QVariant v = static_cast( optionIndex( index ) ); PdmUiCommandSystemProxy::instance()->setUiValueToField( m_uiFieldHandle->uiField(), v ); return true; } } else if ( isMultipleValueField( fieldValue ) ) { std::vector selectedIndices; if ( !m_singleSelectionMode ) { QList fieldValueSelection = fieldValue.toList(); for ( auto v : fieldValueSelection ) { selectedIndices.push_back( v.toUInt() ); } } bool setSelected = value.toBool(); // Do not allow empty selection in single selection mode if ( m_singleSelectionMode ) setSelected = true; unsigned int opIndex = static_cast( optionIndex( index ) ); if ( setSelected ) { bool isIndexPresent = false; for ( auto indexInField : selectedIndices ) { if ( indexInField == opIndex ) { isIndexPresent = true; } } if ( !isIndexPresent ) { selectedIndices.push_back( opIndex ); } } else { m_indexForLastUncheckedItem = index; selectedIndices.erase( std::remove( selectedIndices.begin(), selectedIndices.end(), opIndex ), selectedIndices.end() ); } QList fieldValueSelection; for ( auto v : selectedIndices ) { fieldValueSelection.push_back( QVariant( v ) ); } PdmUiCommandSystemProxy::instance()->setUiValueToField( m_uiFieldHandle->uiField(), fieldValueSelection ); emit dataChanged( index, index ); return true; } } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QModelIndex caf::PdmUiTreeSelectionQModel::indexForLastUncheckedItem() const { return m_indexForLastUncheckedItem; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::clearIndexForLastUncheckedItem() { m_indexForLastUncheckedItem = QModelIndex(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::buildOptionItemTree( int parentOptionIndex, TreeItemType* parentNode ) { if ( parentNode == m_tree ) { for ( int i = 0; i < m_options.size(); i++ ) { if ( m_options[i].level() == 0 ) { TreeItemType* node = new TreeItemType( parentNode, -1, i ); buildOptionItemTree( i, node ); } } } else { int currentOptionIndex = parentOptionIndex + 1; while ( currentOptionIndex < m_options.size() && m_options[currentOptionIndex].level() > m_options[parentNode->dataObject()].level() ) { if ( m_options[currentOptionIndex].level() == m_options[parentNode->dataObject()].level() + 1 ) { TreeItemType* node = new TreeItemType( parentNode, -1, currentOptionIndex ); buildOptionItemTree( currentOptionIndex, node ); } currentOptionIndex++; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::notifyChangedForAllModelIndices() { recursiveNotifyChildren( QModelIndex() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void caf::PdmUiTreeSelectionQModel::recursiveNotifyChildren( const QModelIndex& index ) { for ( int r = 0; r < rowCount( index ); r++ ) { QModelIndex mi = this->index( r, 0, index ); recursiveNotifyChildren( mi ); } if ( index.isValid() ) { emit dataChanged( index, index ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool caf::PdmUiTreeSelectionQModel::isSingleValueField( const QVariant& fieldValue ) { if ( fieldValue.type() == QVariant::Int || fieldValue.type() == QVariant::UInt ) { return true; } return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool caf::PdmUiTreeSelectionQModel::isMultipleValueField( const QVariant& fieldValue ) { if ( fieldValue.type() == QVariant::List ) { return true; } return false; }