Files
ResInsight/Fwk/AppFwk/cafUserInterface/cafPdmUiTreeSelectionEditor.cpp
Magne Sjaastad 40080a99de 9978 Improve UI for long drop-down lists, use tree selection more
* Improve tree selection editor
- always call defineEditorAttributes
- use heightHint in editor attributes 
- use tree selection editor as default editor for std::vector

* Use tree selection editor instead of list selection editor
List selection editor must be used when editing std::vector<cvf::vec3d> and similar. Replace other use of list selection editor with tree selection editor.

* Set checked state based on text string for integer only models
For models with only integer values, use text string to define the items to be selected. The full list will always be visible, and the checked state will be updated when editing the filter text.

Example: "1, 5-7" will set items 1, 5, 6, 7 checked and all other items unchecked

* Minor fixes
- Set placeholder text after content is added (to ensure correct data type)
- Fix check of integers. `canConvert<int>()`returns true for both QString and int. Thus convert to string and then check for int conversion.

* Activate filtering when unchecking all items in list with only integers
- Reactivate filtering when uncheck of all items for a list with only integer values (to keep consistency between filter and list)
- Update function name for clarity

---------

Co-authored-by: Jørgen Herje <jorgen.herje@ceetronsolutions.com>
2023-05-22 15:44:37 +02:00

857 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 "cafQShortenedLabel.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-10" );
}
else
{
m_textFilterLineEdit->setPlaceholderText( "Type to filter items" );
}
// 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() )
{
checkAllItems();
return;
}
unCheckAllItems();
// 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
///
/// Example: 1, 3, 5-10
///
/// 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 minmax = part.split( "-", SkipEmptyParts );
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++ )
{
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;
}
QString searchString = m_textFilterLineEdit->text();
searchString += "*";
// Escape the characters '[' and ']' as these have special meaning for a search string
// To be able to search for vector names in brackets, these must be escaped
// See "Wildcard Matching" in Qt documentation
searchString.replace( "[", "\\[" );
searchString.replace( "]", "\\]" );
QRegExp searcher( searchString, Qt::CaseInsensitive, QRegExp::WildcardUnix );
m_proxyModel->setFilterRegExp( searcher );
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::checkAllItems()
{
QModelIndexList indices = allVisibleSourceModelIndices();
m_model->setCheckedStateForItems( indices, true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiTreeSelectionEditor::unCheckAllItems()
{
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