ResInsight/Fwk/AppFwk/cafUserInterface/cafPdmUiTreeSelectionQModel.cpp
2020-06-19 08:00:01 +02:00

598 lines
21 KiB
C++

//##################################################################################################
//
// 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 <<http://www.gnu.org/licenses/gpl.html>>
// 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 <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#include "cafPdmUiTreeSelectionQModel.h"
#include "cafPdmObject.h"
#include "cafPdmUiCommandSystemProxy.h"
#include "cafPdmUiTreeViewQModel.h"
#include <QAbstractItemModel>
#include <QLabel>
#include <QTreeView>
#include <algorithm>
#include <QDebug>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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<unsigned int> selectedIndices;
{
QVariant fieldValue = m_uiFieldHandle->uiField()->uiValue();
QList<QVariant> 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<unsigned int>( optionIndex( mi ) ) );
}
}
}
else
{
for ( auto mi : sourceModelIndices )
{
const caf::PdmOptionItemInfo* optionItemInfo = optionItem( mi );
if ( !optionItemInfo->isReadOnly() )
{
selectedIndices.erase( static_cast<unsigned int>( optionIndex( mi ) ) );
}
}
}
QList<QVariant> 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<caf::PdmOptionItemInfo>& 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<TreeItemType*>( 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<TreeItemType*>( 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<TreeItemType*>( 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<TreeItemType*>( 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<QVariant> 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<unsigned int>( optionIndex( index ) );
PdmUiCommandSystemProxy::instance()->setUiValueToField( m_uiFieldHandle->uiField(), v );
return true;
}
}
else if ( isMultipleValueField( fieldValue ) )
{
std::vector<unsigned int> selectedIndices;
if ( !m_singleSelectionMode )
{
QList<QVariant> 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<unsigned int>( 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<QVariant> 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;
}