diff --git a/ApplicationLibCode/Application/Tools/RiaStdStringTools.cpp b/ApplicationLibCode/Application/Tools/RiaStdStringTools.cpp index 0ae2ac76b5..d601a05a1c 100644 --- a/ApplicationLibCode/Application/Tools/RiaStdStringTools.cpp +++ b/ApplicationLibCode/Application/Tools/RiaStdStringTools.cpp @@ -309,19 +309,30 @@ std::set RiaStdStringTools::valuesFromRangeSelection( const std::string& s std::istringstream tokenStream( token ); int startIndex, endIndex; - char dash; + char dash, colon; + int step = 1; if ( tokenStream >> startIndex ) { if ( tokenStream >> dash && dash == '-' && tokenStream >> endIndex ) { + if ( tokenStream >> colon && colon == ':' ) + { + tokenStream >> step; + } + + if ( step <= 0 ) + { + step = 1; // Ensure step is positive + } + if ( startIndex > endIndex ) { // If start is greater than end, swap them std::swap( startIndex, endIndex ); } - for ( int i = startIndex; i <= endIndex; ++i ) + for ( int i = startIndex; i <= endIndex; i += step ) { result.insert( i ); } diff --git a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp index 62a5db76c0..e394ee4208 100644 --- a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp +++ b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.cpp @@ -21,11 +21,11 @@ #include "RiaEnsembleNameTools.h" #include "RiaFilePathTools.h" #include "RiaGuiApplication.h" +#include "RiaStdStringTools.h" #include "RiaStringListSerializer.h" #include "RiuFileDialogTools.h" #include "RiuTools.h" -#include "opm/io/eclipse/EclUtil.hpp" #include #include @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -48,19 +47,143 @@ #include #include #include +#include #include #include #include -#define RECURSIVE_FILESEARCH_DEFAULT_DIALOG_HEIGHT 350 -#define FIND_BUTTON_FIND_TEXT "Find" -#define FILES_FOUND_TEXT "Files Found" +// Anonymous namespace for internal functions +namespace +{ + +// Get all first level items, usually the ensemble items +auto firstLevelItems = []( QStandardItem* rootItem ) -> QList +{ + QList firstLevelItems; + + for ( int i = 0; i < rootItem->rowCount(); ++i ) + { + QStandardItem* item = rootItem->child( i ); + if ( item ) + { + firstLevelItems.append( item ); + } + } + + return firstLevelItems; +}; //-------------------------------------------------------------------------------------------------- -/// Internal functions +/// //-------------------------------------------------------------------------------------------------- -static void sortStringsByLength( QStringList& strings, bool ascending = true ); +void setCheckedStateChildItems( QStandardItem* parentItem, Qt::CheckState checkState ) +{ + if ( !parentItem ) return; + + for ( int i = 0; i < parentItem->rowCount(); ++i ) + { + auto childItem = parentItem->child( i ); + if ( childItem && childItem->isCheckable() ) + { + childItem->setCheckState( checkState ); + } + + setCheckedStateChildItems( childItem, checkState ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void findItemsMatching( QStandardItem* parentItem, const QString& substring, QList& matchingItems ) +{ + if ( !parentItem ) return; + + for ( int i = 0; i < parentItem->rowCount(); ++i ) + { + auto searchString = substring + "/"; + + auto childItem = parentItem->child( i ); + if ( childItem ) + { + auto textToMatch = childItem->text(); + textToMatch.replace( '\\', '/' ); + + if ( childItem && textToMatch.contains( searchString, Qt::CaseInsensitive ) ) + { + matchingItems.append( childItem ); + } + } + + findItemsMatching( childItem, substring, matchingItems ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void getTextForCheckedItems( QStandardItem* root, QStandardItem* parentItem, const QString& prefix, QStringList& checkedItems ) +{ + if ( !parentItem ) return; + + for ( int i = 0; i < parentItem->rowCount(); ++i ) + { + auto childItem = parentItem->child( i ); + if ( childItem && childItem->isCheckable() && childItem->checkState() == Qt::Checked ) + { + if ( parentItem != root ) + { + checkedItems.append( prefix + childItem->text() ); + } + + getTextForCheckedItems( root, childItem, prefix, checkedItems ); + } + } +} + +int defaultDialogHeight() +{ + return 550; +} + +QString findButtonText() +{ + return "Search"; +} + +QString filesFoundText() +{ + return "File Selection"; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void sortStringsByLength( QStringList& strings, bool ascending ) +{ + QStringList sorted = strings; + int numItems = sorted.size(); + bool swapped; + do + { + swapped = false; + for ( int i = 0; i < numItems - 1; i++ ) + { + int s0 = strings[i].size(); + int s1 = strings[i + 1].size(); + if ( ( ascending && s0 > s1 ) || ( !ascending && s0 < s1 ) ) + { + const QString temp = strings[i]; + strings[i] = strings[i + 1]; + strings[i + 1] = temp; + swapped = true; + } + } + } while ( swapped ); +} + +} // namespace //-------------------------------------------------------------------------------------------------- /// @@ -145,15 +268,17 @@ RicRecursiveFileSearchDialogResult RicRecursiveFileSearchDialog::runRecursiveSea RiaStringListSerializer stringListSerializer( fileFilterRegistryKey ); stringListSerializer.addString( dialog.m_fileFilterField->currentText(), maxItemsInRegistry ); } + + return { .ok = dialog.result() == QDialog::Accepted, + .files = dialog.m_foundFiles, + .rootDir = dialog.rootDirWithEndSeparator(), + .pathFilter = dialog.pathFilterWithoutStartSeparator(), + .fileNameFilter = dialog.fileNameFilter(), + .fileType = dialog.fileType(), + .groupingMode = dialog.ensembleGroupingMode() }; } - return RicRecursiveFileSearchDialogResult( dialog.result() == QDialog::Accepted, - dialog.m_foundFiles, - dialog.rootDirWithEndSeparator(), - dialog.pathFilterWithoutStartSeparator(), - dialog.fileNameFilter(), - dialog.fileType(), - dialog.ensembleGroupingMode() ); + return {}; } //-------------------------------------------------------------------------------------------------- @@ -180,10 +305,11 @@ RicRecursiveFileSearchDialog::RicRecursiveFileSearchDialog( QWidget* parent, con m_effectiveFilterLabel = new QLabel(); m_effectiveFilterContentLabel = new QLabel(); m_ensembleGroupingMode = new QComboBox(); - m_searchRootLabel = new QLabel(); - m_searchRootContentLabel = new QLabel(); m_findOrCancelButton = new QPushButton(); - m_fileListWidget = new QListWidget(); + m_fileTreeView = new QTreeView(); + m_treeViewFilterLabel = new QLabel( "Selection Filter" ); + m_treeViewFilterLineEdit = new QLineEdit(); + m_treeViewFilterButton = new QPushButton( "Apply" ); m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); @@ -198,11 +324,21 @@ RicRecursiveFileSearchDialog::RicRecursiveFileSearchDialog( QWidget* parent, con connect( m_fileExtensionField, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotFileExtensionChanged( const QString& ) ) ); - connect( m_fileListWidget, + connect( m_fileTreeView, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( slotFileListCustomMenuRequested( const QPoint& ) ) ); + connect( m_treeViewFilterButton, SIGNAL( clicked() ), this, SLOT( slotFilterTreeViewClicked() ) ); + connect( m_treeViewFilterLineEdit, &QLineEdit::returnPressed, m_treeViewFilterButton, &QPushButton::click ); + connect( m_treeViewFilterLineEdit, &QLineEdit::textEdited, m_treeViewFilterButton, &QPushButton::click ); + + m_treeViewFilterLineEdit->setPlaceholderText( "Select Realizations: 1, 5-7, 9-18:3" ); + + m_treeViewFilterLabel->setVisible( false ); + m_treeViewFilterButton->setVisible( false ); + m_treeViewFilterLineEdit->setVisible( false ); + connect( m_browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseButtonClicked() ) ); connect( m_findOrCancelButton, SIGNAL( clicked() ), this, SLOT( slotFindOrCancelButtonClicked() ) ); @@ -210,35 +346,44 @@ RicRecursiveFileSearchDialog::RicRecursiveFileSearchDialog( QWidget* parent, con connect( m_buttons, SIGNAL( accepted() ), this, SLOT( slotDialogOkClicked() ) ); connect( m_buttons, SIGNAL( rejected() ), this, SLOT( slotDialogCancelClicked() ) ); + m_buttons->button( QDialogButtonBox::Ok )->setDefault( true ); + // Set widget properties m_pathFilterLabel->setText( "Path pattern" ); m_fileFilterLabel->setText( "File pattern" ); m_fileTypeLabel->setText( "File type" ); m_fileExtensionLabel->setText( "File extension" ); m_effectiveFilterLabel->setText( "Effective filter" ); - m_searchRootLabel->setText( "Root" ); - m_searchRootLabel->setVisible( false ); m_effectiveFilterContentLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - m_searchRootContentLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - m_searchRootContentLabel->setVisible( false ); - m_fileListWidget->setSelectionMode( QAbstractItemView::ExtendedSelection ); - m_fileListWidget->setVisible( false ); - m_fileListWidget->setContextMenuPolicy( Qt::CustomContextMenu ); - m_fileListWidget->setMinimumHeight( 350 ); + QObject::connect( &m_filePathModel, + &QStandardItemModel::itemChanged, + []( QStandardItem* item ) + { + if ( item->isCheckable() ) + { + setCheckedStateChildItems( item, item->checkState() ); + } + } ); + + m_fileTreeView->setModel( &m_filePathModel ); + m_fileTreeView->setHeaderHidden( true ); + m_fileTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection ); + m_fileTreeView->setContextMenuPolicy( Qt::CustomContextMenu ); + m_fileTreeView->setVisible( false ); + m_fileTreeView->setMinimumHeight( 350 ); m_browseButton->setText( "..." ); m_browseButton->setFixedWidth( 25 ); - m_findOrCancelButton->setText( FIND_BUTTON_FIND_TEXT ); + m_findOrCancelButton->setText( findButtonText() ); m_findOrCancelButton->setFixedWidth( 75 ); - m_findOrCancelButton->setDefault( true ); // Define layout QVBoxLayout* dialogLayout = new QVBoxLayout(); - QGroupBox* inputGroup = new QGroupBox( "Filter" ); + QGroupBox* inputGroup = new QGroupBox( "Search" ); QGridLayout* inputGridLayout = new QGridLayout(); int row = 0; inputGridLayout->addWidget( m_pathFilterLabel, row, 0 ); @@ -277,11 +422,13 @@ RicRecursiveFileSearchDialog::RicRecursiveFileSearchDialog( QWidget* parent, con m_outputGroup = new QGroupBox( "Files Found" ); QGridLayout* outputGridLayout = new QGridLayout(); - outputGridLayout->addWidget( m_searchRootLabel, 1, 0 ); - outputGridLayout->addWidget( m_searchRootContentLabel, 1, 1 ); - // outputGridLayout->addWidget(m_fileListLabel, 2, 0); - outputGridLayout->addWidget( m_fileListWidget, 2, 0, 1, 3 ); + outputGridLayout->addWidget( m_treeViewFilterLabel, 1, 0 ); + outputGridLayout->addWidget( m_treeViewFilterLineEdit, 1, 1 ); + outputGridLayout->addWidget( m_treeViewFilterButton, 1, 2 ); + + outputGridLayout->addWidget( m_fileTreeView, 2, 0, 1, 3 ); + m_outputGroup->setLayout( outputGridLayout ); dialogLayout->addWidget( inputGroup ); @@ -397,7 +544,7 @@ QStringList RicRecursiveFileSearchDialog::fileExtensions() const } QStringList exts = m_fileExtensions; - sortStringsByLength( exts ); + sortStringsByLength( exts, true ); return exts; } @@ -429,10 +576,10 @@ QString RicRecursiveFileSearchDialog::extensionFromFileNameFilter() const //-------------------------------------------------------------------------------------------------- void RicRecursiveFileSearchDialog::updateFileListWidget() { - m_fileListWidget->clear(); - if ( ensembleGroupingMode() != RiaEnsembleNameTools::EnsembleGroupingMode::NONE ) { + m_filePathModel.clear(); + if ( m_fileType == RicRecursiveFileSearchDialog::FileType::STIMPLAN_SUMMARY || m_fileType == RicRecursiveFileSearchDialog::FileType::REVEAL_SUMMARY ) { @@ -440,8 +587,7 @@ void RicRecursiveFileSearchDialog::updateFileListWidget() std::map groupedByEnsemble = RiaEnsembleNameTools::groupFilesByCustomEnsemble( m_foundFiles, fileType ); for ( auto [ensembleName, groupedFileNames] : groupedByEnsemble ) { - new QListWidgetItem( QDir::toNativeSeparators( ensembleName ), m_fileListWidget ); - addToFileListWidget( groupedFileNames ); + addToTreeView( ensembleName, groupedFileNames ); } } else @@ -450,31 +596,39 @@ void RicRecursiveFileSearchDialog::updateFileListWidget() for ( const QStringList& groupedFileNames : groupedByEnsemble ) { QString ensembleName = RiaEnsembleNameTools::findSuitableEnsembleName( groupedFileNames, ensembleGroupingMode() ); - new QListWidgetItem( QDir::toNativeSeparators( ensembleName ), m_fileListWidget ); - addToFileListWidget( groupedFileNames ); + addToTreeView( ensembleName, groupedFileNames ); } } - } - else - { - addToFileListWidget( m_foundFiles ); + + QModelIndex index = m_filePathModel.index( 0, 0 ); + m_fileTreeView->expand( index ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicRecursiveFileSearchDialog::addToFileListWidget( const QStringList& fileNames ) +void RicRecursiveFileSearchDialog::addToTreeView( const QString& ensembleName, const QStringList& fileNames ) { - int rootSearchPathLength = rootDirWithEndSeparator().size(); + auto rootItem = m_filePathModel.invisibleRootItem(); + int rootSearchPathLength = rootDirWithEndSeparator().size(); + + bool isEmpty = m_filePathModel.rowCount() == 0; + + auto ensembleItem = new QStandardItem( QDir::toNativeSeparators( ensembleName ) ); + ensembleItem->setCheckable( true ); + if ( isEmpty ) ensembleItem->setCheckState( Qt::Checked ); + rootItem->appendRow( ensembleItem ); for ( const auto& fileName : fileNames ) { - QString itemText = fileName; + auto itemText = fileName; itemText.remove( 0, rootSearchPathLength ); - QListWidgetItem* item = new QListWidgetItem( QDir::toNativeSeparators( itemText ), m_fileListWidget ); - item->setFlags( item->flags() | Qt::ItemIsUserCheckable ); - item->setCheckState( Qt::Checked ); + + auto fileItem = new QStandardItem( QDir::toNativeSeparators( itemText ) ); + fileItem->setCheckable( true ); + if ( isEmpty ) fileItem->setCheckState( Qt::Checked ); + ensembleItem->appendRow( fileItem ); } } @@ -484,8 +638,8 @@ void RicRecursiveFileSearchDialog::addToFileListWidget( const QStringList& fileN void RicRecursiveFileSearchDialog::clearFileList() { m_foundFiles.clear(); - m_fileListWidget->clear(); - m_outputGroup->setTitle( FILES_FOUND_TEXT ); + m_filePathModel.clear(); + m_outputGroup->setTitle( filesFoundText() ); setOkButtonEnabled( false ); } @@ -530,8 +684,12 @@ void RicRecursiveFileSearchDialog::updateStatus( Status status, const QString& e lastStatusUpdate = now; - m_fileListWidget->clear(); - new QListWidgetItem( newStatus, m_fileListWidget ); + m_filePathModel.clear(); + + // Show status message in the tree view + auto rootItem = m_filePathModel.invisibleRootItem(); + auto ensembleItem = new QStandardItem( newStatus ); + rootItem->appendRow( ensembleItem ); } //-------------------------------------------------------------------------------------------------- @@ -708,7 +866,6 @@ void RicRecursiveFileSearchDialog::updateEffectiveFilter() // Present native separators to the user m_effectiveFilterContentLabel->setText( QDir::toNativeSeparators( effFilterText ) ); - m_searchRootContentLabel->setText( QDir::toNativeSeparators( rootDirWithEndSeparator() ) ); } //-------------------------------------------------------------------------------------------------- @@ -728,7 +885,6 @@ void RicRecursiveFileSearchDialog::warningIfInvalidCharacters() { QToolTip::showText( m_fileFilterField->mapToGlobal( QPoint( 0, 0 ) ), "File pattern contains invalid characters" ); m_effectiveFilterContentLabel->setText( "(Invalid filter)" ); - m_searchRootContentLabel->setText( "" ); } else { @@ -743,7 +899,7 @@ void RicRecursiveFileSearchDialog::slotPathFilterChanged( const QString& text ) { updateEffectiveFilter(); warningIfInvalidCharacters(); - m_findOrCancelButton->setDefault( true ); + m_findOrCancelButton->setFocus(); } //-------------------------------------------------------------------------------------------------- @@ -754,7 +910,7 @@ void RicRecursiveFileSearchDialog::slotFileFilterChanged( const QString& text ) clearFileList(); updateEffectiveFilter(); warningIfInvalidCharacters(); - m_findOrCancelButton->setDefault( true ); + m_findOrCancelButton->setFocus(); } //-------------------------------------------------------------------------------------------------- @@ -806,7 +962,7 @@ void RicRecursiveFileSearchDialog::slotFileListCustomMenuRequested( const QPoint connect( action, SIGNAL( triggered() ), SLOT( slotToggleFileListItems() ) ); menu.addAction( action ); - QPoint globalPoint = m_fileListWidget->mapToGlobal( point ); + QPoint globalPoint = m_fileTreeView->mapToGlobal( point ); menu.exec( globalPoint ); } @@ -815,11 +971,17 @@ void RicRecursiveFileSearchDialog::slotFileListCustomMenuRequested( const QPoint //-------------------------------------------------------------------------------------------------- void RicRecursiveFileSearchDialog::slotToggleFileListItems() { - for ( auto& item : m_fileListWidget->selectedItems() ) + auto selectionModel = m_fileTreeView->selectionModel(); + auto indices = selectionModel->selectedIndexes(); + for ( auto& index : indices ) { - if ( ( item->flags() & Qt::ItemIsUserCheckable ) != 0 ) + if ( index.isValid() ) { - item->setCheckState( item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked ); + auto item = m_filePathModel.itemFromIndex( index ); + if ( item && item->isCheckable() ) + { + item->setCheckState( item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked ); + } } } } @@ -829,11 +991,17 @@ void RicRecursiveFileSearchDialog::slotToggleFileListItems() //-------------------------------------------------------------------------------------------------- void RicRecursiveFileSearchDialog::slotTurnOffFileListItems() { - for ( auto& item : m_fileListWidget->selectedItems() ) + auto selectionModel = m_fileTreeView->selectionModel(); + auto indices = selectionModel->selectedIndexes(); + for ( auto& index : indices ) { - if ( ( item->flags() & Qt::ItemIsUserCheckable ) != 0 ) + if ( index.isValid() ) { - item->setCheckState( Qt::Unchecked ); + auto item = m_filePathModel.itemFromIndex( index ); + if ( item && item->isCheckable() ) + { + item->setCheckState( Qt::Unchecked ); + } } } } @@ -843,11 +1011,17 @@ void RicRecursiveFileSearchDialog::slotTurnOffFileListItems() //-------------------------------------------------------------------------------------------------- void RicRecursiveFileSearchDialog::slotTurnOnFileListItems() { - for ( auto& item : m_fileListWidget->selectedItems() ) + auto selectionModel = m_fileTreeView->selectionModel(); + auto indices = selectionModel->selectedIndexes(); + for ( auto& index : indices ) { - if ( ( item->flags() & Qt::ItemIsUserCheckable ) != 0 ) + if ( index.isValid() ) { - item->setCheckState( Qt::Checked ); + auto item = m_filePathModel.itemFromIndex( index ); + if ( item && item->isCheckable() ) + { + item->setCheckState( Qt::Checked ); + } } } } @@ -857,10 +1031,17 @@ void RicRecursiveFileSearchDialog::slotTurnOnFileListItems() //-------------------------------------------------------------------------------------------------- void RicRecursiveFileSearchDialog::slotCopyFileItemText() { - if ( m_fileListWidget->currentItem() ) + auto index = m_fileTreeView->currentIndex(); { - QString relativePathText = m_fileListWidget->currentItem()->text(); - QApplication::clipboard()->setText( relativePathText ); + if ( index.isValid() ) + { + auto item = m_filePathModel.itemFromIndex( index ); + if ( item ) + { + QString relativePathText = item->text(); + QApplication::clipboard()->setText( relativePathText ); + } + } } } @@ -869,17 +1050,19 @@ void RicRecursiveFileSearchDialog::slotCopyFileItemText() //-------------------------------------------------------------------------------------------------- void RicRecursiveFileSearchDialog::slotFindOrCancelButtonClicked() { - if ( m_findOrCancelButton->text() == FIND_BUTTON_FIND_TEXT ) + if ( m_findOrCancelButton->text() == findButtonText() ) { clearFileList(); - if ( !m_fileListWidget->isVisible() ) + m_treeViewFilterLabel->setVisible( true ); + m_treeViewFilterButton->setVisible( true ); + m_treeViewFilterLineEdit->setVisible( true ); + + if ( !m_fileTreeView->isVisible() ) { - m_fileListWidget->setVisible( true ); - m_searchRootLabel->setVisible( true ); - m_searchRootContentLabel->setVisible( true ); + m_fileTreeView->setVisible( true ); - if ( height() < RECURSIVE_FILESEARCH_DEFAULT_DIALOG_HEIGHT ) resize( width(), RECURSIVE_FILESEARCH_DEFAULT_DIALOG_HEIGHT ); + if ( height() < defaultDialogHeight() ) resize( width(), defaultDialogHeight() ); } m_findOrCancelButton->setText( "Cancel" ); @@ -897,7 +1080,7 @@ void RicRecursiveFileSearchDialog::slotFindOrCancelButtonClicked() updateFileListWidget(); - m_findOrCancelButton->setText( FIND_BUTTON_FIND_TEXT ); + m_findOrCancelButton->setText( findButtonText() ); if ( m_isCancelPressed ) { @@ -909,11 +1092,14 @@ void RicRecursiveFileSearchDialog::slotFindOrCancelButtonClicked() } else { - m_outputGroup->setTitle( QString( "%1 (%2)" ).arg( FILES_FOUND_TEXT ).arg( m_foundFiles.size() ) ); + m_outputGroup->setTitle( QString( "%1 (%2)" ).arg( filesFoundText() ).arg( m_foundFiles.size() ) ); } setOkButtonEnabled( !m_foundFiles.isEmpty() ); - m_buttons->button( QDialogButtonBox::Ok )->setDefault( true ); + + m_buttons->button( QDialogButtonBox::Ok )->setFocus(); + + slotFilterTreeViewClicked(); } else { @@ -924,19 +1110,49 @@ void RicRecursiveFileSearchDialog::slotFindOrCancelButtonClicked() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicRecursiveFileSearchDialog::slotDialogOkClicked() +void RicRecursiveFileSearchDialog::slotFilterTreeViewClicked() { - m_foundFiles.clear(); + QString filterText = m_treeViewFilterLineEdit->text(); + + auto values = RiaStdStringTools::valuesFromRangeSelection( filterText.toStdString() ); - int itemCount = m_fileListWidget->count(); - for ( int i = 0; i < itemCount; i++ ) + auto items = firstLevelItems( m_filePathModel.invisibleRootItem() ); + for ( auto item : items ) { - const QListWidgetItem* item = m_fileListWidget->item( i ); - if ( ( item->flags() & Qt::ItemIsUserCheckable ) != 0 && item->checkState() ) + if ( item->checkState() == Qt::Unchecked ) continue; + + if ( filterText.isEmpty() ) { - m_foundFiles.push_back( rootDirWithEndSeparator() + RiaFilePathTools::toInternalSeparator( item->text() ) ); + setCheckedStateChildItems( item, Qt::Checked ); + } + else + { + setCheckedStateChildItems( item, Qt::Unchecked ); + + for ( auto val : values ) + { + QString searchString = "realization-" + QString::number( val ); + + QList matchingItems; + findItemsMatching( item, searchString, matchingItems ); + for ( auto item : matchingItems ) + { + item->setCheckState( Qt::Checked ); + } + } } } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRecursiveFileSearchDialog::slotDialogOkClicked() +{ + QStringList checkedItems; + getTextForCheckedItems( m_filePathModel.invisibleRootItem(), m_filePathModel.invisibleRootItem(), rootDirWithEndSeparator(), checkedItems ); + + m_foundFiles = checkedItems; accept(); } @@ -950,6 +1166,15 @@ void RicRecursiveFileSearchDialog::slotDialogCancelClicked() reject(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRecursiveFileSearchDialog::showEvent( QShowEvent* event ) +{ + m_findOrCancelButton->setFocus(); + QDialog::showEvent( event ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -998,100 +1223,6 @@ QStringList RicRecursiveFileSearchDialog::fileTypeToExtensionStrings( const std: return extensions; } -//-------------------------------------------------------------------------------------------------- -/// Internal functions -//-------------------------------------------------------------------------------------------------- - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void sortStringsByLength( QStringList& strings, bool ascending /*= true*/ ) -{ - QStringList sorted = strings; - int numItems = sorted.size(); - bool swapped; - do - { - swapped = false; - for ( int i = 0; i < numItems - 1; i++ ) - { - int s0 = strings[i].size(); - int s1 = strings[i + 1].size(); - if ( ( ascending && s0 > s1 ) || ( !ascending && s0 < s1 ) ) - { - const QString temp = strings[i]; - strings[i] = strings[i + 1]; - strings[i + 1] = temp; - swapped = true; - } - } - } while ( swapped ); -} - -//-------------------------------------------------------------------------------------------------- -/// Obsolete -//-------------------------------------------------------------------------------------------------- -QStringList RicRecursiveFileSearchDialog::buildDirectoryListRecursive( const QString& currentDir, int level ) -{ - QStringList allDirs; - - if ( m_isCancelPressed ) return allDirs; - - QString currPathFilter = pathFilterWithoutStartSeparator(); - bool subStringFilter = false; - - // Optimizing for speed by a refined match at first directory level - if ( level == 1 ) - { - QString pathFilter = pathFilterWithoutStartSeparator(); - if ( !pathFilter.startsWith( "*" ) ) - { - int wildcardIndex = pathFilter.indexOf( QRegularExpression( QString( "[*%1]" ).arg( RiaFilePathTools::separator() ) ) ); - if ( wildcardIndex >= 0 ) - { - currPathFilter = pathFilter.left( wildcardIndex + 1 ); - subStringFilter = true; - } - } - } - - QString currRelPath = RiaFilePathTools::relativePath( rootDirWithEndSeparator(), currentDir ); - if ( pathFilterMatch( currPathFilter, currRelPath ) ) - { - allDirs.push_back( currentDir ); - } - else if ( level == 1 && subStringFilter ) - { - return QStringList(); - } - - QDir qdir( currentDir ); - QStringList subDirs = qdir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ); - for ( QString subDir : subDirs ) - { - QString subDirFullPath = qdir.absoluteFilePath( subDir ); - updateStatus( SEARCHING_FOR_DIRS, subDirFullPath ); - QApplication::processEvents(); - allDirs += buildDirectoryListRecursive( subDirFullPath, level + 1 ); - } - return m_isCancelPressed ? QStringList() : allDirs; -} - -//-------------------------------------------------------------------------------------------------- -/// Obsolete -//-------------------------------------------------------------------------------------------------- -bool RicRecursiveFileSearchDialog::pathFilterMatch( const QString& pathFilter, const QString& relPath ) -{ - QString pattern = pathFilter; - if ( relPath.endsWith( RiaFilePathTools::separator() ) && !pathFilter.endsWith( RiaFilePathTools::separator() ) ) - pattern += RiaFilePathTools::separator(); - - QRegularExpression regexp( pattern, QRegularExpression::CaseInsensitiveOption ); - auto match = regexp.match( relPath ); - - return match.hasMatch(); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -1167,7 +1298,6 @@ RiaDefines::FileType RicRecursiveFileSearchDialog::mapSummaryFileType( RicRecurs return RiaDefines::FileType::STIMPLAN_SUMMARY; default: { - CAF_ASSERT( false && "Unsupported file type" ); return RiaDefines::FileType::SMSPEC; } } diff --git a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h index 98ead09e78..f9aac443e9 100644 --- a/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h +++ b/ApplicationLibCode/Commands/RicRecursiveFileSearchDialog.h @@ -26,6 +26,7 @@ #include "Summary/RiaSummaryDefines.h" #include +#include class QLabel; class QLineEdit; @@ -33,12 +34,12 @@ class QTextEdit; class QDialogButtonBox; class QPushButton; class QMainWindow; -class QListWidget; class QGroupBox; class QComboBox; class QCheckBox; +class QTreeView; -class RicRecursiveFileSearchDialogResult; +struct RicRecursiveFileSearchDialogResult; //================================================================================================== /// @@ -101,7 +102,7 @@ class RicRecursiveFileSearchDialog : public QDialog void updateFileListWidget(); void clearFileList(); - void addToFileListWidget( const QStringList& fileNames ); + void addToTreeView( const QString& ensembleName, const QStringList& fileNames ); // File search methods @@ -123,6 +124,7 @@ private slots: void slotBrowseButtonClicked(); void slotUseRealizationStarClicked(); void slotFindOrCancelButtonClicked(); + void slotFilterTreeViewClicked(); void slotFileListCustomMenuRequested( const QPoint& point ); void slotCopyFileItemText(); @@ -133,6 +135,8 @@ private slots: void slotDialogOkClicked(); void slotDialogCancelClicked(); + void showEvent( QShowEvent* event ) override; + private: QLabel* m_pathFilterLabel; QComboBox* m_pathFilterField; @@ -154,10 +158,14 @@ private slots: QComboBox* m_ensembleGroupingMode; - QGroupBox* m_outputGroup; - QLabel* m_searchRootLabel; - QLabel* m_searchRootContentLabel; - QListWidget* m_fileListWidget; + QGroupBox* m_outputGroup; + + QLabel* m_treeViewFilterLabel; + QLineEdit* m_treeViewFilterLineEdit; + QPushButton* m_treeViewFilterButton; + + QTreeView* m_fileTreeView; + QStandardItemModel m_filePathModel; QDialogButtonBox* m_buttons; @@ -167,35 +175,13 @@ private slots: FileType m_fileType; bool m_isCancelPressed; - - // Obsolete. Here for reference if this search mode is needed later - QStringList buildDirectoryListRecursive( const QString& currentDir, int level = 0 ); - bool pathFilterMatch( const QString& pathFilter, const QString& relPath ); }; //================================================================================================== /// //================================================================================================== -class RicRecursiveFileSearchDialogResult +struct RicRecursiveFileSearchDialogResult { -public: - RicRecursiveFileSearchDialogResult( bool ok, - const QStringList& files, - const QString& rootDir, - const QString& pathFilter, - const QString& fileNameFilter, - RicRecursiveFileSearchDialog::FileType fileType, - RiaEnsembleNameTools::EnsembleGroupingMode groupingMode ) - : ok( ok ) - , files( files ) - , rootDir( rootDir ) - , pathFilter( pathFilter ) - , fileNameFilter( fileNameFilter ) - , fileType( fileType ) - , groupingMode( groupingMode ) - { - } - bool ok; QStringList files; QString rootDir; diff --git a/ApplicationLibCode/ProjectDataModel/CellFilters/RimCellFilterIntervalTool.cpp b/ApplicationLibCode/ProjectDataModel/CellFilters/RimCellFilterIntervalTool.cpp index f82ed7f8ad..3920ec368a 100644 --- a/ApplicationLibCode/ProjectDataModel/CellFilters/RimCellFilterIntervalTool.cpp +++ b/ApplicationLibCode/ProjectDataModel/CellFilters/RimCellFilterIntervalTool.cpp @@ -107,6 +107,9 @@ size_t RimCellFilterIntervalTool::numberFromPart( std::string strVal ) const // Define a range with the comma separated format A,B,C-D, etc., i.e. 1,4,5-8 // Only numbers > 0 are supported. // For a range with increment > 1, use i.e. 4-8:2 +// +// Related code in RiaStdStringTools::valuesFromRangeSelection( const std::string& s ) +// //-------------------------------------------------------------------------------------------------- void RimCellFilterIntervalTool::setInterval( bool enabled, std::string intervalText ) { diff --git a/ApplicationLibCode/UnitTests/RiaStdStringTools-Test.cpp b/ApplicationLibCode/UnitTests/RiaStdStringTools-Test.cpp index da1435e10c..ca7ac91c56 100644 --- a/ApplicationLibCode/UnitTests/RiaStdStringTools-Test.cpp +++ b/ApplicationLibCode/UnitTests/RiaStdStringTools-Test.cpp @@ -231,6 +231,39 @@ TEST( RiaStdStringToolsTest, ValuesFromRangeSelectionMinMax ) ASSERT_EQ( expectedValues, actualValues ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RiaStdStringToolsTest, ValuesWithStep ) +{ + { + std::string testString = "1, 4-10:2,13-15"; + std::set expectedValues = { 1, 4, 6, 8, 10, 13, 14, 15 }; + + auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString ); + + ASSERT_EQ( expectedValues, actualValues ); + } + + { + std::string testString = "4-6:invalid"; + std::set expectedValues = { 4, 5, 6 }; + + auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString ); + + ASSERT_EQ( expectedValues, actualValues ); + } + + { + std::string testString = "b-5"; + std::set expectedValues = {}; + + auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString ); + + ASSERT_EQ( expectedValues, actualValues ); + } +} + //-------------------------------------------------------------------------------------------------- /// //--------------------------------------------------------------------------------------------------