Files
ResInsight/Fwk/AppFwk/cafUserInterface/cafPdmUiTreeSelectionEditor.cpp
Magne Sjaastad 1d57b9032b Custom vfp plot (#11450)
* AppFwk: When clearing a tree selection, make sure all values are cleared
* Fix deprecated implicit lambda
* Add support for using the closest value in addition to exact match
* Add table data source object and add plot with multiple data sources
Delete the temporary RimVfpDeck class
Add RimVfpTable to represent a table in a data source
Add plot able to show data from multiple tables

* AppFwk: Make it possible to call resolveReferences multiple times
Use case: Vfp tables are stored in files. Multiple tables can be present in one file. Pdm table objects are created after resolve references is done as part of parsing file. When the Pdm object are created, resolveReferences can be called once more.

* Call resolveReferencesRecursively() after RimVfpTable objects are created
2024-05-29 12:55:45 +02:00

883 lines
30 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 "cafPdmUiTreeSelectionEditor.h"
#include "cafAssert.h"
#include "cafPdmObject.h"
#include "cafPdmUiCommandSystemProxy.h"
#include "cafPdmUiTreeSelectionQModel.h"
#include <QBoxLayout>
#include <QCheckBox>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QPainter>
#include <QPalette>
#include <QSortFilterProxyModel>
#include <QStyleOption>
#include <QTimer>
#include <QTreeView>
#include <algorithm>
namespace caf
{
//==================================================================================================
/// Helper class used to control height of size hint
//==================================================================================================
class QTreeViewHeightHint : public QTreeView
{
public:
explicit QTreeViewHeightHint( QWidget* parent = nullptr )
: m_heightHint( -1 )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize sizeHint() const override
{
QSize mySize = QTreeView::sizeHint();
if ( m_heightHint > 0 )
{
mySize.setHeight( m_heightHint );
}
return mySize;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void setHeightHint( int heightHint ) { m_heightHint = heightHint; }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void keyPressEvent( QKeyEvent* event ) override
{
QTreeView::keyPressEvent( event );
if ( event->key() == Qt::Key_Down || event->key() == Qt::Key_Up || event->key() == Qt::Key_Home ||
event->key() == Qt::Key_End || event->key() == Qt::Key_PageDown || event->key() == Qt::Key_PageUp )
{
emit clicked( currentIndex() );
}
}
private:
int m_heightHint;
};
//==================================================================================================
///
//==================================================================================================
class FilterLeafNodesOnlyProxyModel : public QSortFilterProxyModel
{
public:
FilterLeafNodesOnlyProxyModel( QObject* parent = nullptr )
: QSortFilterProxyModel( parent )
{
}
protected:
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool filterAcceptsRow( int source_row, const QModelIndex& source_parent ) const override
{
QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
if ( sourceModel()->hasChildren( index ) )
{
// Always include node if node has children
return true;
}
return QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent );
}
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
CAF_PDM_UI_FIELD_EDITOR_SOURCE_INIT( PdmUiTreeSelectionEditor );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
PdmUiTreeSelectionEditor::PdmUiTreeSelectionEditor()
: m_model( nullptr )
, m_proxyModel( nullptr )
, m_useSingleSelectionMode( false )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
PdmUiTreeSelectionEditor::~PdmUiTreeSelectionEditor()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::configureAndUpdateUi( const QString& uiConfigName )
{
// Label
CAF_ASSERT( !m_label.isNull() );
PdmUiFieldEditorHandle::updateLabelFromField( m_label, uiConfigName );
if ( !m_model )
{
m_model = new caf::PdmUiTreeSelectionQModel( this );
m_proxyModel = new FilterLeafNodesOnlyProxyModel( this );
m_proxyModel->setSourceModel( m_model );
m_proxyModel->setFilterCaseSensitivity( Qt::CaseInsensitive );
m_treeView->setModel( m_proxyModel );
}
QList<PdmOptionItemInfo> options = uiField()->valueOptions();
bool itemCountHasChaged = false;
if ( m_model->optionItemCount() != options.size() ) itemCountHasChaged = true;
QVariant fieldValue = uiField()->uiValue();
m_model->setUiValueCache( &fieldValue );
// TODO: If the count is different between incoming and current list of items,
// use cafQTreeViewStateSerializer to restore collapsed state
m_model->setOptions( this, options );
if ( itemCountHasChaged )
{
m_treeView->expandAll();
}
m_useSingleSelectionMode = PdmUiTreeSelectionQModel::isSingleValueField( fieldValue );
caf::PdmUiObjectHandle* uiObject = uiObj( uiField()->fieldHandle()->ownerObject() );
if ( uiObject )
{
uiObject->editorAttribute( uiField()->fieldHandle(), uiConfigName, &m_attributes );
}
if ( PdmUiTreeSelectionQModel::isMultipleValueField( fieldValue ) )
{
m_useSingleSelectionMode = m_attributes.singleSelectionMode;
if ( !m_attributes.showTextFilter )
{
m_textFilterLineEdit->hide();
}
if ( m_attributes.singleSelectionMode || !m_attributes.showToggleAllCheckbox )
{
m_toggleAllCheckBox->hide();
}
else
{
if ( options.empty() )
{
m_toggleAllCheckBox->setChecked( false );
}
else
{
QModelIndexList indices = allVisibleSourceModelIndices();
if ( !indices.empty() )
{
size_t editableItems = 0u;
size_t checkedEditableItems = 0u;
for ( auto mi : indices )
{
if ( !m_model->isReadOnly( mi ) )
{
editableItems++;
if ( m_model->isChecked( mi ) )
{
checkedEditableItems++;
}
}
}
bool allItemsChecked = ( editableItems > 0u && checkedEditableItems == editableItems );
m_toggleAllCheckBox->setChecked( allItemsChecked );
}
}
}
}
if ( m_useSingleSelectionMode )
{
m_treeView->setSelectionMode( QAbstractItemView::SingleSelection );
m_treeView->setContextMenuPolicy( Qt::NoContextMenu );
m_model->enableSingleSelectionMode( m_attributes.singleSelectionMode );
m_toggleAllCheckBox->hide();
}
else
{
m_treeView->setSelectionMode( QAbstractItemView::ExtendedSelection );
}
if ( m_attributes.heightHint > 0 )
{
m_treeView->setHeightHint( m_attributes.heightHint );
}
// If the tree doesn't have grand children we treat this as a straight list
m_treeView->setRootIsDecorated( m_model->hasGrandChildren() );
m_model->resetUiValueCache();
if ( m_attributes.currentIndexFieldHandle )
{
PdmUiFieldHandle* uiFieldHandle = m_attributes.currentIndexFieldHandle->uiCapability();
if ( uiFieldHandle )
{
QModelIndexList indices = allVisibleSourceModelIndices();
QVariant currentItemValue = uiFieldHandle->uiValue();
for ( const auto& mi : indices )
{
QVariant itemValue = m_model->data( mi, PdmUiTreeSelectionQModel::optionItemValueRole() );
if ( currentItemValue == itemValue )
{
QModelIndex treeViewIndex = m_proxyModel->mapFromSource( mi );
m_treeView->setCurrentIndex( treeViewIndex );
}
}
}
}
// Set placeholder text based on content
if ( hasOnlyIntegers( m_model ) )
{
m_textFilterLineEdit->setPlaceholderText( "Integer filter e.g. 1, 5-7, 9-18:3" );
}
else
{
m_textFilterLineEdit->setPlaceholderText( "Type to filter items" );
}
// Set the CSS property to use radio buttons instead of check boxes
// This is done by setting the "state" property to "UseRadioButtons"
QString stateText;
if ( m_useSingleSelectionMode )
{
stateText = "UseRadioButtons";
}
std::string propertyName = "state";
auto propertyValue = m_treeView->property( propertyName.data() ).toString();
if ( propertyValue != stateText )
{
m_treeView->setProperty( propertyName.data(), stateText );
m_treeView->style()->unpolish( m_treeView );
m_treeView->style()->polish( m_treeView );
m_treeView->update();
}
// It is required to use a timer here, as the layout of the widgets are handled by events
// Calling scrollTo() here has no effect, or scrolls to wrong location
QTimer::singleShot( 150, this, SLOT( slotScrollToFirstCheckedItem() ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* PdmUiTreeSelectionEditor::createEditorWidget( QWidget* parent )
{
QFrame* frame = new QFrame( parent );
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins( 0, 0, 0, 0 );
frame->setLayout( layout );
{
QHBoxLayout* headerLayout = new QHBoxLayout;
headerLayout->setContentsMargins( 0, 0, 0, 0 );
layout->addLayout( headerLayout );
PdmUiTreeSelectionEditorAttribute attrib;
m_toggleAllCheckBox = new QCheckBox();
headerLayout->addWidget( m_toggleAllCheckBox );
connect( m_toggleAllCheckBox, SIGNAL( clicked( bool ) ), this, SLOT( slotToggleAll() ) );
m_textFilterLineEdit = new QLineEdit();
headerLayout->addWidget( m_textFilterLineEdit );
connect( m_textFilterLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( slotTextFilterChanged() ) );
}
QTreeViewHeightHint* treeViewHeightHint = new QTreeViewHeightHint( parent );
treeViewHeightHint->setHeightHint( 2000 );
treeViewHeightHint->setHeaderHidden( true );
m_treeView = treeViewHeightHint;
m_treeView->setContextMenuPolicy( Qt::CustomContextMenu );
connect( m_treeView, SIGNAL( customContextMenuRequested( QPoint ) ), SLOT( customMenuRequested( QPoint ) ) );
connect( m_treeView, SIGNAL( clicked( QModelIndex ) ), this, SLOT( slotClicked( QModelIndex ) ), Qt::UniqueConnection );
layout->addWidget( treeViewHeightHint );
return frame;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* PdmUiTreeSelectionEditor::createLabelWidget( QWidget* parent )
{
m_label = new QShortenedLabel( parent );
return m_label;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QMargins PdmUiTreeSelectionEditor::calculateLabelContentMargins() const
{
QSize editorSize = m_textFilterLineEdit->sizeHint();
QSize labelSize = m_label->sizeHint();
int heightDiff = editorSize.height() - labelSize.height();
QMargins contentMargins = m_label->contentsMargins();
if ( heightDiff > 0 )
{
contentMargins.setTop( contentMargins.top() + heightDiff / 2 );
contentMargins.setBottom( contentMargins.bottom() + heightDiff / 2 );
}
return contentMargins;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool PdmUiTreeSelectionEditor::isMultiRowEditor() const
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::customMenuRequested( const QPoint& pos )
{
QMenu menu;
QModelIndexList selectedIndexes = m_treeView->selectionModel()->selectedIndexes();
bool onlyHeadersInSelection = true;
for ( auto mi : selectedIndexes )
{
QVariant v = m_proxyModel->data( mi, PdmUiTreeSelectionQModel::headingRole() );
if ( v.toBool() == false )
{
onlyHeadersInSelection = false;
}
}
if ( onlyHeadersInSelection && !selectedIndexes.empty() )
{
{
QAction* act = new QAction( "Sub Items On", this );
connect( act, SIGNAL( triggered() ), SLOT( slotSetSubItemsOn() ) );
menu.addAction( act );
}
{
QAction* act = new QAction( "Sub Items Off", this );
connect( act, SIGNAL( triggered() ), SLOT( slotSetSubItemsOff() ) );
menu.addAction( act );
}
}
else if ( !selectedIndexes.empty() )
{
{
QAction* act = new QAction( "Set Selected Checked", this );
connect( act, SIGNAL( triggered() ), SLOT( slotSetSelectedOn() ) );
menu.addAction( act );
}
{
QAction* act = new QAction( "Set Selected Unchecked", this );
connect( act, SIGNAL( triggered() ), SLOT( slotSetSelectedOff() ) );
menu.addAction( act );
}
menu.addSeparator();
if ( selectedIndexes.size() == 1 )
{
QAction* act = new QAction( "Invert Checked State Of All", this );
connect( act, SIGNAL( triggered() ), SLOT( slotInvertCheckedStateOfAll() ) );
menu.addAction( act );
}
else
{
QAction* act = new QAction( "Invert Checked States of Selected", this );
connect( act, SIGNAL( triggered() ), SLOT( slotInvertCheckedStateForSelection() ) );
menu.addAction( act );
}
}
if ( !menu.actions().empty() )
{
// Qt doc: QAbstractScrollArea and its subclasses that map the context menu event to coordinates of the viewport().
QPoint globalPos = m_treeView->viewport()->mapToGlobal( pos );
menu.exec( globalPos );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSelectedOn()
{
this->setCheckedStateOfSelected( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSelectedOff()
{
this->setCheckedStateOfSelected( false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::setCheckedStateOfSelected( bool checked )
{
if ( !m_proxyModel ) return;
QItemSelection selectionInProxyModel = m_treeView->selectionModel()->selection();
QItemSelection selectionInSourceModel = m_proxyModel->mapSelectionToSource( selectionInProxyModel );
m_model->setCheckedStateForItems( selectionInSourceModel.indexes(), checked );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSubItemsOn()
{
this->setCheckedStateForSubItemsOfSelected( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotSetSubItemsOff()
{
this->setCheckedStateForSubItemsOfSelected( false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::setCheckedStateForSubItemsOfSelected( bool checked )
{
QModelIndexList selectedProxyIndexes = m_treeView->selectionModel()->selectedIndexes();
QModelIndexList sourceIndexesToSubItems;
for ( auto mi : selectedProxyIndexes )
{
for ( int i = 0; i < m_proxyModel->rowCount( mi ); i++ )
{
QModelIndex childProxyIndex = m_proxyModel->index( i, 0, mi );
sourceIndexesToSubItems.push_back( m_proxyModel->mapToSource( childProxyIndex ) );
}
}
m_model->setCheckedStateForItems( sourceIndexesToSubItems, checked );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotToggleAll()
{
if ( m_toggleAllCheckBox->isChecked() )
{
checkAllItemsMatchingFilter();
return;
}
if ( m_textFilterLineEdit->text().isEmpty() )
{
m_model->unselectAllItems();
}
else
{
unCheckAllItemsMatchingFilter();
}
// Apply integer filtering if the model contains only integers
if ( hasOnlyIntegers( m_model ) )
{
setCheckedStateForIntegerItemsMatchingFilter();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotInvertCheckedStateForSelection()
{
QItemSelection selectionInProxyModel = m_treeView->selectionModel()->selection();
QItemSelection selectionInSourceModel = m_proxyModel->mapSelectionToSource( selectionInProxyModel );
m_model->invertCheckedStateForItems( selectionInSourceModel.indexes() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotInvertCheckedStateOfAll()
{
QModelIndexList indices = allVisibleSourceModelIndices();
m_model->invertCheckedStateForItems( indices );
}
//--------------------------------------------------------------------------------------------------
/// Parse the filter text based on the following rules:
/// 1. A comma separated list of integers
/// 2. A range of integers separated by a dash
/// 3. An optional step value for a range
///
/// Example: 1, 3, 5-10, 4-10:2
///
/// Mark matching items as checked
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::setCheckedStateForIntegerItemsMatchingFilter()
{
#if ( QT_VERSION < QT_VERSION_CHECK( 5, 14, 0 ) )
auto SkipEmptyParts = QString::SkipEmptyParts;
#else
auto SkipEmptyParts = Qt::SkipEmptyParts;
#endif
std::set<int> filterValues;
QString searchString = m_textFilterLineEdit->text();
QStringList parts = searchString.split( ",", SkipEmptyParts );
for ( auto& part : parts )
{
QStringList subparts = part.split( ":", SkipEmptyParts );
QStringList minmax = subparts.front().split( "-", SkipEmptyParts );
int step = 1;
if ( subparts.size() > 1 )
{
step = subparts.back().toInt();
if ( step < 1 ) step = 1;
}
switch ( minmax.size() )
{
case 1:
{
auto firstValueText = minmax.front();
filterValues.insert( firstValueText.toInt() );
break;
}
case 2:
{
auto firstValue = minmax.front().toInt();
auto secondValue = minmax.back().toInt();
for ( int val = firstValue; val <= secondValue; val += step )
{
filterValues.insert( val );
}
break;
}
default:
break;
}
}
QModelIndexList indices = allVisibleSourceModelIndices();
QModelIndexList indicesToSetChecked;
QModelIndexList indicesToSetUnChecked;
for ( const auto& mi : indices )
{
auto data = mi.data();
if ( data.canConvert<int>() )
{
auto value = data.toInt();
if ( filterValues.find( value ) != filterValues.end() )
{
indicesToSetChecked.push_back( mi );
}
else
{
indicesToSetUnChecked.push_back( mi );
}
}
}
m_model->setCheckedStateForItems( indicesToSetChecked, true );
m_model->setCheckedStateForItems( indicesToSetUnChecked, false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotTextFilterChanged()
{
if ( hasOnlyIntegers( m_model ) )
{
setCheckedStateForIntegerItemsMatchingFilter();
return;
}
auto regExpString = m_textFilterLineEdit->text() + ".*";
QRegularExpression regExp( regExpString );
regExp.setPatternOptions( QRegularExpression::CaseInsensitiveOption );
m_proxyModel->setFilterRegularExpression( regExp );
updateUi();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotClicked( const QModelIndex& index )
{
QModelIndex lastUncheckedIndex = m_model->indexForLastUncheckedItem();
m_model->clearIndexForLastUncheckedItem();
QModelIndex proxyModelIndex = m_proxyModel->mapFromSource( lastUncheckedIndex );
if ( proxyModelIndex == index )
{
// Early return to avoid changing the current item if an item was unchecked
return;
}
if ( m_attributes.setCurrentIndexWhenItemIsChecked && index.isValid() )
{
QModelIndexList selectedIndexes = m_treeView->selectionModel()->selectedIndexes();
if ( selectedIndexes.size() < 2 )
{
QVariant v = m_proxyModel->data( index, Qt::CheckStateRole );
if ( v == Qt::Checked )
{
m_treeView->setCurrentIndex( index );
}
}
}
currentChanged( index );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::slotScrollToFirstCheckedItem()
{
auto firstVisibleIndex = m_treeView->indexAt( m_treeView->viewport()->rect().topLeft() );
auto lastVisibleIndex = m_treeView->indexAt( m_treeView->viewport()->rect().bottomRight() );
if ( !firstVisibleIndex.isValid() )
{
return;
}
if ( !lastVisibleIndex.isValid() )
{
return;
}
for ( int i = firstVisibleIndex.row(); i < lastVisibleIndex.row(); i++ )
{
auto treeViewIndex = m_proxyModel->index( i, 0 );
if ( m_proxyModel->data( treeViewIndex, Qt::CheckStateRole ).toBool() )
{
// Do nothing if there is a checked and visible item in the view
return;
}
}
for ( int i = 0; i < m_proxyModel->rowCount(); i++ )
{
auto treeViewIndex = m_proxyModel->index( i, 0 );
if ( m_proxyModel->data( treeViewIndex, Qt::CheckStateRole ).toBool() )
{
// Scroll to the first checked item if no checked items are visible
m_treeView->scrollTo( treeViewIndex );
return;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::currentChanged( const QModelIndex& current )
{
if ( m_useSingleSelectionMode )
{
m_proxyModel->setData( current, true, Qt::CheckStateRole );
}
if ( m_attributes.currentIndexFieldHandle )
{
PdmUiFieldHandle* uiFieldHandle = m_attributes.currentIndexFieldHandle->uiCapability();
if ( uiFieldHandle )
{
QVariant v = m_proxyModel->data( current, PdmUiTreeSelectionQModel::optionItemValueRole() );
PdmUiCommandSystemProxy::instance()->setUiValueToField( uiFieldHandle, v );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::checkAllItemsMatchingFilter()
{
QModelIndexList indices = allVisibleSourceModelIndices();
m_model->setCheckedStateForItems( indices, true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::unCheckAllItemsMatchingFilter()
{
QModelIndexList indices = allVisibleSourceModelIndices();
m_model->setCheckedStateForItems( indices, false );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QModelIndexList PdmUiTreeSelectionEditor::allVisibleSourceModelIndices() const
{
QModelIndexList indices;
recursiveAppendVisibleSourceModelIndices( QModelIndex(), &indices );
return indices;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::recursiveAppendVisibleSourceModelIndices( const QModelIndex& parent,
QModelIndexList* sourceModelIndices ) const
{
for ( int row = 0; row < m_proxyModel->rowCount( parent ); row++ )
{
QModelIndex mi = m_proxyModel->index( row, 0, parent );
if ( mi.isValid() )
{
QVariant v = m_proxyModel->data( mi, PdmUiTreeSelectionQModel::headingRole() );
if ( v.toBool() == false )
{
sourceModelIndices->push_back( m_proxyModel->mapToSource( mi ) );
}
recursiveAppendVisibleSourceModelIndices( mi, sourceModelIndices );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool PdmUiTreeSelectionEditor::hasOnlyIntegers( const QAbstractItemModel* model )
{
if ( !model ) return false;
// Lambda function to check if a string is an integer
auto isInteger = []( const QString& s ) -> bool
{
bool ok;
s.toInt( &ok );
return ok;
};
for ( int row = 0; row < model->rowCount(); ++row )
{
for ( int column = 0; column < model->columnCount(); ++column )
{
const QModelIndex index = model->index( row, column );
if ( index.isValid() )
{
QVariant data = index.data();
if ( !data.canConvert<QString>() || !isInteger( data.toString() ) )
{
return false;
}
}
}
}
return true;
}
} // end namespace caf