mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 06:38:13 -05:00
CellFilters: Add case-level Data Filters with per-view wrappers
Introduce RimDataFilterCollection on RimEclipseCase to hold filter configuration that is shared across all views of the case. Each RimEclipseView gets a RimDataFilterInViewCollection of RimDataFilterInView wrappers that auto-sync with the case-level list and expose an independent on/off toggle per view. The wrapper's applyToCellVisibility delegates to the wrapped RimCellFilter via the existing unified virtual, so property, range, and combined filters all work uniformly. RimDataFilterCollection is built on caf::PdmObjectCollection<RimCellFilter> and emits a filtersChanged signal on add/remove/reorder/child changes. The wrapper collection mirrors RimSurfaceInViewCollection's sync pattern: new wrappers default to checked, removed sources drop their wrappers, and the wrapper list follows the case order. Per-view wrappers feed into RivReservoirViewPartMgr::computePropertyVisibility after the existing view-level property-filter loop. The RicNew*Feature commands now route into a selected RimDataFilterCollection, and the existing combined-filter path is generalized to allow combined filters living under either a per-view RimEclipsePropertyFilterCollection or the new case-level collection. Several Asserted ancestor lookups in RimEclipsePropertyFilter, RimCellFilter and RimEclipseResultDefinition that assumed a Rim3dView ancestor were relaxed to the non-asserting variant with graceful fallbacks, so case-level filters can be created and edited without a host view. Backwards compatible: empty Data Filter collection is a no-op, existing per-view RimEclipsePropertyFilterCollection and RimCellFilterCollection paths are unchanged. GeoMech parity deferred.
This commit is contained in:
1 parent
3375e37ffc
commit
4ed6d70b73
28 files changed
+1333
-105
No files matched your search
@@ -21,11 +21,13 @@
|
||||
#include "Rim3dView.h"
|
||||
#include "RimCase.h"
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
|
||||
#include "Riu3DMainWindowTools.h"
|
||||
|
||||
#include "cafSelectionManagerTools.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -45,15 +47,34 @@ bool addNewFilterIfCombinedSelected( Init&& init )
|
||||
std::vector<RimCombinedFilter*> combined = caf::selectedObjectsByTypeStrict<RimCombinedFilter*>();
|
||||
if ( combined.empty() ) return false;
|
||||
|
||||
RimCombinedFilter* target = combined.front();
|
||||
if ( auto* view = target->firstAncestorOrThisOfType<Rim3dView>() )
|
||||
{
|
||||
if ( view->ownerCase() )
|
||||
{
|
||||
T* created = target->addNewFilter<T>( std::forward<Init>( init ) );
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
}
|
||||
}
|
||||
// Combined filters may live under a view OR under the case-level RimDataFilterCollection. The
|
||||
// combined filter's own m_srcCase is propagated to new children via addNewFilter, so we don't
|
||||
// need a Rim3dView ancestor here.
|
||||
RimCombinedFilter* target = combined.front();
|
||||
T* created = target->addNewFilter<T>( std::forward<Init>( init ) );
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// If a RimDataFilterCollection (case-level) is the current selection, create a new T inside it,
|
||||
/// configure it via init, and select the result. Returns true when a data-filter collection was the
|
||||
/// selection — the caller should bail out of its own collection-targeted flow.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
template <typename T, typename Init>
|
||||
bool addNewFilterToDataCollectionIfSelected( Init&& init )
|
||||
{
|
||||
static_assert( std::is_base_of_v<RimCellFilter, T>, "T must derive from RimCellFilter" );
|
||||
|
||||
std::vector<RimDataFilterCollection*> selected = caf::selectedObjectsByTypeStrict<RimDataFilterCollection*>();
|
||||
if ( selected.empty() ) return false;
|
||||
|
||||
RimDataFilterCollection* target = selected.front();
|
||||
|
||||
auto* created = new T();
|
||||
target->addFilter( created );
|
||||
std::forward<Init>( init )( created );
|
||||
Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
return true;
|
||||
}
|
||||
} // namespace RicCellFilterFeatureTools
|
||||
@@ -55,6 +55,7 @@ bool RicNewCellIndexFilterFeature::isCommandEnabled() const
|
||||
void RicNewCellIndexFilterFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
if ( RicCellFilterFeatureTools::addNewFilterIfCombinedSelected<RimCellIndexFilter>( []( RimCellIndexFilter* ) {} ) ) return;
|
||||
if ( RicCellFilterFeatureTools::addNewFilterToDataCollectionIfSelected<RimCellIndexFilter>( []( RimCellIndexFilter* ) {} ) ) return;
|
||||
|
||||
// Find the selected Cell Filter Collection
|
||||
std::vector<RimCellFilterCollection*> colls = caf::selectedObjectsByTypeStrict<RimCellFilterCollection*>();
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "RimCase.h"
|
||||
#include "RimCellFilterCollection.h"
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
#include "RimGridView.h"
|
||||
#include "RimPolygonFilter.h"
|
||||
|
||||
@@ -38,6 +39,19 @@
|
||||
|
||||
CAF_CMD_SOURCE_INIT( RicNewPolygonFilterFeature, "RicNewPolygonFilterFeature" );
|
||||
|
||||
namespace
|
||||
{
|
||||
void configurePolygonFilter( RimPolygonFilter* f, RimPolygon* polygon )
|
||||
{
|
||||
f->setPolygon( polygon );
|
||||
f->configurePolygonEditor();
|
||||
if ( polygon )
|
||||
f->enableFilter( true );
|
||||
else
|
||||
f->enablePicking( true );
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -68,38 +82,38 @@ void RicNewPolygonFilterFeature::onActionTriggered( bool isChecked )
|
||||
polygons = selectedPolygons();
|
||||
}
|
||||
|
||||
// If a combined filter is selected, add polygon filters as its children.
|
||||
// If a combined filter is selected, add polygon filters as its children. Combined filters can
|
||||
// live either under a view (RimCellFilterCollection / RimEclipsePropertyFilterCollection) or
|
||||
// under the case-level RimDataFilterCollection — both are supported, so no Rim3dView ancestor
|
||||
// is required here. The combined filter's own m_srcCase drives propagation into new children.
|
||||
auto* combined = caf::SelectionManager::instance()->selectedItemOfType<RimCombinedFilter>();
|
||||
if ( combined )
|
||||
{
|
||||
RimCase* srcCase = combined->firstAncestorOrThisOfTypeAsserted<Rim3dView>()->ownerCase();
|
||||
if ( !srcCase ) return;
|
||||
|
||||
auto addOne = [combined]( RimPolygon* polygon )
|
||||
{
|
||||
return combined->addNewFilter<RimPolygonFilter>(
|
||||
[polygon]( RimPolygonFilter* f )
|
||||
{
|
||||
f->setPolygon( polygon );
|
||||
f->configurePolygonEditor();
|
||||
if ( polygon )
|
||||
f->enableFilter( true );
|
||||
else
|
||||
f->enablePicking( true );
|
||||
} );
|
||||
};
|
||||
{ return combined->addNewFilter<RimPolygonFilter>( [polygon]( RimPolygonFilter* f ) { configurePolygonFilter( f, polygon ); } ); };
|
||||
|
||||
if ( polygons.empty() ) polygons.push_back( nullptr );
|
||||
RimPolygonFilter* lastItem = nullptr;
|
||||
if ( polygons.empty() )
|
||||
for ( auto polygon : polygons )
|
||||
{
|
||||
lastItem = addOne( nullptr );
|
||||
lastItem = addOne( polygon );
|
||||
}
|
||||
else
|
||||
if ( lastItem ) Riu3DMainWindowTools::selectAsCurrentItem( lastItem );
|
||||
return;
|
||||
}
|
||||
|
||||
// If a case-level Data Filter Collection is selected, add polygon filters there.
|
||||
auto* dataCollection = caf::SelectionManager::instance()->selectedItemOfType<RimDataFilterCollection>();
|
||||
if ( dataCollection )
|
||||
{
|
||||
if ( polygons.empty() ) polygons.push_back( nullptr );
|
||||
RimPolygonFilter* lastItem = nullptr;
|
||||
for ( auto polygon : polygons )
|
||||
{
|
||||
for ( auto polygon : polygons )
|
||||
{
|
||||
lastItem = addOne( polygon );
|
||||
}
|
||||
auto* f = new RimPolygonFilter();
|
||||
dataCollection->addFilter( f );
|
||||
configurePolygonFilter( f, polygon );
|
||||
lastItem = f;
|
||||
}
|
||||
if ( lastItem ) Riu3DMainWindowTools::selectAsCurrentItem( lastItem );
|
||||
return;
|
||||
|
||||
@@ -51,14 +51,15 @@ RicNewRangeFilterSliceFeature::RicNewRangeFilterSliceFeature( QString cmdText, Q
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicNewRangeFilterSliceFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
const int sliceDir = m_sliceDirection;
|
||||
if ( RicCellFilterFeatureTools::addNewFilterIfCombinedSelected<RimCellRangeFilter>(
|
||||
[sliceDir]( RimCellRangeFilter* f )
|
||||
{
|
||||
f->setGridIndex( 0 );
|
||||
f->setDefaultValues( sliceDir, -1 );
|
||||
} ) )
|
||||
return;
|
||||
const int sliceDir = m_sliceDirection;
|
||||
auto initSlice = [sliceDir]( RimCellRangeFilter* f )
|
||||
{
|
||||
f->setGridIndex( 0 );
|
||||
f->setDefaultValues( sliceDir, -1 );
|
||||
};
|
||||
|
||||
if ( RicCellFilterFeatureTools::addNewFilterIfCombinedSelected<RimCellRangeFilter>( initSlice ) ) return;
|
||||
if ( RicCellFilterFeatureTools::addNewFilterToDataCollectionIfSelected<RimCellRangeFilter>( initSlice ) ) return;
|
||||
|
||||
RimCellFilterCollection* filterCollection = nullptr;
|
||||
RimCase* sourceCase = nullptr;
|
||||
|
||||
@@ -40,6 +40,7 @@ CAF_CMD_SOURCE_INIT( RicNewUserDefinedFilterFeature, "RicNewUserDefinedFilterFea
|
||||
void RicNewUserDefinedFilterFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
if ( RicCellFilterFeatureTools::addNewFilterIfCombinedSelected<RimUserDefinedFilter>( []( RimUserDefinedFilter* ) {} ) ) return;
|
||||
if ( RicCellFilterFeatureTools::addNewFilterToDataCollectionIfSelected<RimUserDefinedFilter>( []( RimUserDefinedFilter* ) {} ) ) return;
|
||||
|
||||
// Find the selected Cell Filter Collection
|
||||
std::vector<RimCellFilterCollection*> colls = caf::selectedObjectsByTypeStrict<RimCellFilterCollection*>();
|
||||
|
||||
@@ -41,6 +41,8 @@ void RicNewUserDefinedIndexFilterFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
if ( RicCellFilterFeatureTools::addNewFilterIfCombinedSelected<RimUserDefinedIndexFilter>( []( RimUserDefinedIndexFilter* ) {} ) )
|
||||
return;
|
||||
if ( RicCellFilterFeatureTools::addNewFilterToDataCollectionIfSelected<RimUserDefinedIndexFilter>( []( RimUserDefinedIndexFilter* ) {} ) )
|
||||
return;
|
||||
|
||||
// Find the selected Cell Filter Collection
|
||||
std::vector<RimCellFilterCollection*> colls = caf::selectedObjectsByTypeStrict<RimCellFilterCollection*>();
|
||||
|
||||
+23
-14
@@ -23,6 +23,7 @@
|
||||
#include "Rim3dView.h"
|
||||
#include "RimCase.h"
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
#include "RimEclipsePropertyFilterCollection.h"
|
||||
|
||||
#include "Riu3DMainWindowTools.h"
|
||||
@@ -38,12 +39,13 @@ CAF_CMD_SOURCE_INIT( RicEclipseCombinedPropertyFilterNewFeature, "RicEclipseComb
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicEclipseCombinedPropertyFilterNewFeature::isCommandEnabled() const
|
||||
{
|
||||
// Enable on either the collection or a combined filter within the collection (for nesting).
|
||||
// Enable on either the collection, a combined filter (for nesting, regardless of whether the
|
||||
// host collection is per-view or case-level), or a case-level data filter collection directly.
|
||||
auto combined = caf::selectedObjectsByTypeStrict<RimCombinedFilter*>();
|
||||
if ( !combined.empty() && combined.front()->firstAncestorOrThisOfType<RimEclipsePropertyFilterCollection>() )
|
||||
{
|
||||
return RicEclipsePropertyFilterFeatureImpl::isPropertyFilterCommandAvailable( combined.front() );
|
||||
}
|
||||
if ( !combined.empty() ) return true;
|
||||
|
||||
auto dataColls = caf::selectedObjectsByTypeStrict<RimDataFilterCollection*>();
|
||||
if ( !dataColls.empty() ) return true;
|
||||
|
||||
auto filterCollections = RicEclipsePropertyFilterFeatureImpl::selectedPropertyFilterCollections();
|
||||
if ( filterCollections.size() == 1 )
|
||||
@@ -58,18 +60,25 @@ bool RicEclipseCombinedPropertyFilterNewFeature::isCommandEnabled() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicEclipseCombinedPropertyFilterNewFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
// Nested combined filter: add the new one inside the currently selected combined filter.
|
||||
// Nested combined filter: add the new one inside the currently selected combined filter,
|
||||
// regardless of whether the host is per-view or case-level. The combined filter's m_srcCase
|
||||
// propagates into new children automatically.
|
||||
auto combined = caf::selectedObjectsByTypeStrict<RimCombinedFilter*>();
|
||||
if ( !combined.empty() && combined.front()->firstAncestorOrThisOfType<RimEclipsePropertyFilterCollection>() )
|
||||
if ( !combined.empty() )
|
||||
{
|
||||
RimCombinedFilter* parent = combined.front();
|
||||
RimCase* srcCase = parent->firstAncestorOrThisOfTypeAsserted<Rim3dView>()->ownerCase();
|
||||
if ( srcCase )
|
||||
{
|
||||
RimCombinedFilter* created = parent->addNewFilter<RimCombinedFilter>( []( RimCombinedFilter* ) {} );
|
||||
parent->updateConnectedEditors();
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
}
|
||||
RimCombinedFilter* created = parent->addNewFilter<RimCombinedFilter>( []( RimCombinedFilter* ) {} );
|
||||
parent->updateConnectedEditors();
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
return;
|
||||
}
|
||||
|
||||
// Case-level data filter collection: add a new combined filter directly there.
|
||||
auto dataColls = caf::selectedObjectsByTypeStrict<RimDataFilterCollection*>();
|
||||
if ( !dataColls.empty() )
|
||||
{
|
||||
RimCombinedFilter* created = dataColls.front()->addNewCombinedFilter();
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
#include "RicEclipsePropertyFilterNewExec.h"
|
||||
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
#include "RimEclipsePropertyFilter.h"
|
||||
#include "RimEclipsePropertyFilterCollection.h"
|
||||
|
||||
#include "Riu3DMainWindowTools.h"
|
||||
|
||||
#include "cafCmdExecCommandManager.h"
|
||||
#include "cafSelectionManagerTools.h"
|
||||
|
||||
@@ -38,12 +41,14 @@ CAF_CMD_SOURCE_INIT( RicEclipsePropertyFilterNewFeature, "RicEclipsePropertyFilt
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicEclipsePropertyFilterNewFeature::isCommandEnabled() const
|
||||
{
|
||||
// Enabled if either a property-filter collection is selected, or a combined filter inside one is selected.
|
||||
// Enabled if either a property-filter collection is selected, a combined filter (in either a
|
||||
// per-view or a case-level host) is selected, or a case-level data filter collection is
|
||||
// selected.
|
||||
auto combined = caf::selectedObjectsByTypeStrict<RimCombinedFilter*>();
|
||||
if ( !combined.empty() && combined.front()->firstAncestorOrThisOfType<RimEclipsePropertyFilterCollection>() )
|
||||
{
|
||||
return RicEclipsePropertyFilterFeatureImpl::isPropertyFilterCommandAvailable( combined.front() );
|
||||
}
|
||||
if ( !combined.empty() ) return RicEclipsePropertyFilterFeatureImpl::isPropertyFilterCommandAvailable( combined.front() );
|
||||
|
||||
auto dataColls = caf::selectedObjectsByTypeStrict<RimDataFilterCollection*>();
|
||||
if ( !dataColls.empty() ) return true;
|
||||
|
||||
std::vector<RimEclipsePropertyFilterCollection*> filterCollections =
|
||||
RicEclipsePropertyFilterFeatureImpl::selectedPropertyFilterCollections();
|
||||
@@ -61,9 +66,29 @@ bool RicEclipsePropertyFilterNewFeature::isCommandEnabled() const
|
||||
void RicEclipsePropertyFilterNewFeature::onActionTriggered( bool isChecked )
|
||||
{
|
||||
auto combined = caf::selectedObjectsByTypeStrict<RimCombinedFilter*>();
|
||||
if ( !combined.empty() && combined.front()->firstAncestorOrThisOfType<RimEclipsePropertyFilterCollection>() )
|
||||
if ( !combined.empty() )
|
||||
{
|
||||
RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter( combined.front() );
|
||||
RimCombinedFilter* parent = combined.front();
|
||||
if ( parent->firstAncestorOrThisOfType<RimEclipsePropertyFilterCollection>() )
|
||||
{
|
||||
RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter( parent );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case-level combined filter: setCase propagation (via parent->addFilter →
|
||||
// child->setCase) binds the new filter's result definition to the eclipse case.
|
||||
auto* created = parent->addNewFilter<RimEclipsePropertyFilter>( []( RimEclipsePropertyFilter* /*f*/ ) {} );
|
||||
parent->updateConnectedEditors();
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto dataColls = caf::selectedObjectsByTypeStrict<RimDataFilterCollection*>();
|
||||
if ( !dataColls.empty() )
|
||||
{
|
||||
auto* created = dataColls.front()->addNewPropertyFilter();
|
||||
if ( created ) Riu3DMainWindowTools::selectAsCurrentItem( created );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include "RimCellEdgeColors.h"
|
||||
#include "RimCellFilter.h"
|
||||
#include "RimCellFilterCollection.h"
|
||||
#include "RimDataFilterInView.h"
|
||||
#include "RimDataFilterInViewCollection.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEclipseCellColors.h"
|
||||
#include "RimEclipsePropertyFilter.h"
|
||||
@@ -854,7 +856,12 @@ void RivReservoirViewPartMgr::computePropertyVisibility( cvf::UByteArray*
|
||||
// Copy if not equal
|
||||
if ( cellVisibility != rangeFilterVisibility ) ( *cellVisibility ) = *rangeFilterVisibility;
|
||||
|
||||
if ( !propFilterColl->hasActiveFilters() ) return;
|
||||
// Static method: the host view is reached via the property filter collection's ancestry.
|
||||
RimEclipseView* view = propFilterColl->firstAncestorOrThisOfType<RimEclipseView>();
|
||||
RimDataFilterInViewCollection* dataInView = view ? view->dataFiltersInView() : nullptr;
|
||||
const bool hasCaseLevelFilters = dataInView && dataInView->hasActiveFilters();
|
||||
|
||||
if ( !propFilterColl->hasActiveFilters() && !hasCaseLevelFilters ) return;
|
||||
|
||||
for ( RimCellFilter* filter : propFilterColl->filtersForEvaluation() )
|
||||
{
|
||||
@@ -863,6 +870,14 @@ void RivReservoirViewPartMgr::computePropertyVisibility( cvf::UByteArray*
|
||||
filter->applyToCellVisibility( cellVisibility, grid, timeStepIndex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( hasCaseLevelFilters )
|
||||
{
|
||||
for ( RimDataFilterInView* wrapper : dataInView->activeWrappers() )
|
||||
{
|
||||
wrapper->applyToCellVisibility( cellVisibility, grid, timeStepIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -3,6 +3,9 @@ set(SOURCE_GROUP_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCellFilterCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCellRangeFilter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCombinedFilter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterInView.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterInViewCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimPropertyFilter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimPropertyFilterCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEclipsePropertyFilter.h
|
||||
@@ -21,6 +24,9 @@ set(SOURCE_GROUP_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCellFilterCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCellRangeFilter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimCombinedFilter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterInView.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterInViewCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimPropertyFilter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimPropertyFilterCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimEclipsePropertyFilter.cpp
|
||||
|
||||
@@ -123,6 +123,15 @@ void RimCellFilter::triggerFilterChanged() const
|
||||
filterChanged.send();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Renames go through the inherited m_name field; emit filterChanged so any RimCombinedFilter
|
||||
/// hosting this filter can refresh its auto-derived display name.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimCellFilter::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& /*oldValue*/, const QVariant& /*newValue*/ )
|
||||
{
|
||||
if ( changedField == nameField() ) triggerFilterChanged();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Is the cell filter doing active filtering, or is it just showning outline, etc. in the view
|
||||
/// - isActive == true -> filter enabled in explorer
|
||||
@@ -275,7 +284,14 @@ QString RimCellFilter::modeString() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const cvf::StructGridInterface* RimCellFilter::selectedGrid() const
|
||||
{
|
||||
auto rimCase = firstAncestorOrThisOfTypeAsserted<Rim3dView>()->ownerCase();
|
||||
// Case-level data filters have no Rim3dView ancestor; fall back to m_srcCase, which the
|
||||
// owning RimDataFilterCollection / RimCombinedFilter propagates on add.
|
||||
RimCase* rimCase = nullptr;
|
||||
if ( auto* rimView = firstAncestorOrThisOfType<Rim3dView>() )
|
||||
{
|
||||
rimCase = rimView->ownerCase();
|
||||
}
|
||||
if ( !rimCase ) rimCase = m_srcCase();
|
||||
if ( !rimCase ) return nullptr;
|
||||
|
||||
int clampedIndex = gridIndex();
|
||||
@@ -385,7 +401,8 @@ void RimCellFilter::applyToCellVisibility( cvf::UByteArray* cellVisibility, cons
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimCellFilter::isFilterControlled() const
|
||||
{
|
||||
auto rimView = firstAncestorOrThisOfTypeAsserted<Rim3dView>();
|
||||
// Case-level data filters have no view ancestor; treat as not controlled.
|
||||
auto rimView = firstAncestorOrThisOfType<Rim3dView>();
|
||||
|
||||
bool isFilterControlled = false;
|
||||
if ( rimView && rimView->viewController() && rimView->viewController()->isCellFiltersControlled() )
|
||||
|
||||
@@ -102,7 +102,8 @@ public:
|
||||
protected:
|
||||
caf::PdmFieldHandle* userDescriptionField() override;
|
||||
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
||||
bool isFilterControlled() const;
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
bool isFilterControlled() const;
|
||||
|
||||
RimGeoMechCase* geoMechCase() const;
|
||||
RimEclipseCase* eclipseCase() const;
|
||||
|
||||
@@ -129,6 +129,8 @@ QString RimCellRangeFilter::fullName() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimCellRangeFilter::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
||||
{
|
||||
RimCellFilter::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( changedField == &m_gridIndex )
|
||||
{
|
||||
const cvf::StructGridInterface* grid = selectedGrid();
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimDataFilterCollection.h"
|
||||
|
||||
#include "RimCase.h"
|
||||
#include "RimCellRangeFilter.h"
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimEclipsePropertyFilter.h"
|
||||
|
||||
#include "cafCmdFeatureMenuBuilder.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimDataFilterCollection, "DataFilterCollection" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterCollection::RimDataFilterCollection()
|
||||
: filtersChanged( this )
|
||||
{
|
||||
CAF_PDM_InitObject( "Data Filters", ":/CellFilter.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_items, "Filters", "Filters" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_srcCase, "SourceCase", "Source Case" );
|
||||
m_srcCase.uiCapability()->setUiHidden( true );
|
||||
m_srcCase.xmlCapability()->disableIO();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterCollection::~RimDataFilterCollection() = default;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::setCase( RimCase* srcCase )
|
||||
{
|
||||
m_srcCase = srcCase;
|
||||
for ( RimCellFilter* child : items() )
|
||||
{
|
||||
if ( child ) child->setCase( srcCase );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimCase* RimDataFilterCollection::ownerCase() const
|
||||
{
|
||||
if ( m_srcCase() ) return m_srcCase();
|
||||
return firstAncestorOfType<RimCase>();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Programmatic removal path: deleteItem in the templated base does not call onItemsChanged, so
|
||||
/// we send filtersChanged ourselves. UI-driven detachment routes through onChildDeleted instead.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::removeFilter( RimCellFilter* f )
|
||||
{
|
||||
if ( !f ) return;
|
||||
deleteItem( f );
|
||||
filtersChanged.send();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEclipsePropertyFilter* RimDataFilterCollection::addNewPropertyFilter()
|
||||
{
|
||||
auto* propertyFilter = new RimEclipsePropertyFilter();
|
||||
addFilter( propertyFilter );
|
||||
return propertyFilter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimCellRangeFilter* RimDataFilterCollection::addNewRangeFilter()
|
||||
{
|
||||
// No setDefaultValues call: it asserts on a Rim3dView ancestor, which the case-level
|
||||
// collection does not have. Constructor defaults (1,1,1,1,1,1) are used as-is.
|
||||
auto* rangeFilter = new RimCellRangeFilter();
|
||||
addFilter( rangeFilter );
|
||||
return rangeFilter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimCombinedFilter* RimDataFilterCollection::addNewCombinedFilter()
|
||||
{
|
||||
auto* combined = new RimCombinedFilter();
|
||||
addFilter( combined );
|
||||
return combined;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimDataFilterCollection::hasActiveFilters() const
|
||||
{
|
||||
for ( RimCellFilter* filter : items() )
|
||||
{
|
||||
if ( filter && filter->isFilterEnabled() ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Hook from caf::PdmObjectCollection<T>: invoked after addItem / insertItem and on UI-driven
|
||||
/// changes to m_items. caf::Signal::connect silently skips a re-connect when the same observer
|
||||
/// is already registered (cafSignal.h:141), so iterating over all items each time is safe.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::onItemsChanged()
|
||||
{
|
||||
for ( RimCellFilter* child : items() )
|
||||
{
|
||||
connectChildSignal( child );
|
||||
if ( child && m_srcCase() ) child->setCase( m_srcCase() );
|
||||
}
|
||||
filtersChanged.send();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Hook from caf::PdmObjectCollection<T>: PDM invokes this after a child is detached from
|
||||
/// m_items. Forward as filtersChanged so view-side wrappers reconcile.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects )
|
||||
{
|
||||
updateConnectedEditors();
|
||||
filtersChanged.send();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::initAfterRead()
|
||||
{
|
||||
for ( RimCellFilter* child : items() )
|
||||
{
|
||||
connectChildSignal( child );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
|
||||
{
|
||||
menuBuilder << "RicEclipsePropertyFilterNewFeature";
|
||||
menuBuilder << "Separator";
|
||||
menuBuilder.subMenuStart( "Range Filter" );
|
||||
menuBuilder << "RicNewRangeFilterSliceIFeature";
|
||||
menuBuilder << "RicNewRangeFilterSliceJFeature";
|
||||
menuBuilder << "RicNewRangeFilterSliceKFeature";
|
||||
menuBuilder << "RicNewCellRangeFilterFeature";
|
||||
menuBuilder.subMenuEnd();
|
||||
menuBuilder << "RicNewCellIndexFilterFeature";
|
||||
menuBuilder << "Separator";
|
||||
menuBuilder << "RicNewUserDefinedFilterFeature";
|
||||
menuBuilder << "RicNewUserDefinedIndexFilterFeature";
|
||||
menuBuilder << "Separator";
|
||||
menuBuilder << "RicEclipseCombinedPropertyFilterNewFeature";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::connectChildSignal( RimCellFilter* child )
|
||||
{
|
||||
if ( !child ) return;
|
||||
child->filterChanged.connect( this, &RimDataFilterCollection::onChildFilterChanged );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterCollection::onChildFilterChanged( const caf::SignalEmitter* /*emitter*/ )
|
||||
{
|
||||
filtersChanged.send();
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RimCellFilter.h"
|
||||
|
||||
#include "cafPdmObjectCollection.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
#include "cafSignal.h"
|
||||
|
||||
class RimCase;
|
||||
class RimCellRangeFilter;
|
||||
class RimCombinedFilter;
|
||||
class RimEclipsePropertyFilter;
|
||||
|
||||
//==================================================================================================
|
||||
/// Case-level container of data filters. Filters configured here are shared by all views of the
|
||||
/// owning case via per-view RimDataFilterInView wrappers, which expose an independent on/off
|
||||
/// toggle without duplicating filter configuration. Mode-less: combine semantics live in
|
||||
/// RimCombinedFilter children when needed.
|
||||
//==================================================================================================
|
||||
class RimDataFilterCollection : public caf::PdmObjectCollection<RimCellFilter>
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
caf::Signal<> filtersChanged; // add / remove / reorder / child changed
|
||||
|
||||
RimDataFilterCollection();
|
||||
~RimDataFilterCollection() override;
|
||||
|
||||
void setCase( RimCase* srcCase );
|
||||
RimCase* ownerCase() const;
|
||||
|
||||
std::vector<RimCellFilter*> filters() const { return items(); }
|
||||
void addFilter( RimCellFilter* f ) { addItem( f ); }
|
||||
void removeFilter( RimCellFilter* f );
|
||||
|
||||
RimEclipsePropertyFilter* addNewPropertyFilter();
|
||||
RimCellRangeFilter* addNewRangeFilter();
|
||||
RimCombinedFilter* addNewCombinedFilter();
|
||||
|
||||
bool hasActiveFilters() const;
|
||||
|
||||
protected:
|
||||
void onItemsChanged() override;
|
||||
void onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects ) override;
|
||||
void initAfterRead() override;
|
||||
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
|
||||
|
||||
private:
|
||||
void connectChildSignal( RimCellFilter* child );
|
||||
void onChildFilterChanged( const caf::SignalEmitter* emitter );
|
||||
|
||||
caf::PdmPtrField<RimCase*> m_srcCase;
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimDataFilterInView.h"
|
||||
|
||||
#include "Rim3dView.h"
|
||||
#include "RimCellFilter.h"
|
||||
#include "RimGridView.h"
|
||||
|
||||
#include "cafPdmUiTreeOrdering.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimDataFilterInView, "DataFilterInView" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterInView::RimDataFilterInView()
|
||||
{
|
||||
CAF_PDM_InitObject( "Data Filter", ":/CellFilter.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_sourceFilter, "SourceFilter", "Source Filter" );
|
||||
m_sourceFilter.uiCapability()->setUiHidden( true );
|
||||
|
||||
setCheckState( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterInView::~RimDataFilterInView() = default;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimCellFilter* RimDataFilterInView::sourceFilter() const
|
||||
{
|
||||
return m_sourceFilter();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::setSourceFilter( RimCellFilter* sourceFilter )
|
||||
{
|
||||
m_sourceFilter = sourceFilter;
|
||||
syncNameFromSource();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimDataFilterInView::isEvaluatable() const
|
||||
{
|
||||
if ( !isChecked() ) return false;
|
||||
if ( !m_sourceFilter() ) return false;
|
||||
return m_sourceFilter()->isFilterEnabled();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Forward to the wrapped source filter. The unified RimCellFilter::applyToCellVisibility virtual
|
||||
/// already handles property (uses timeStep), range (legacy bridge), and combined (recurses)
|
||||
/// uniformly, so no per-type branching is needed here.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::applyToCellVisibility( cvf::UByteArray* cellVisibility, const RigGridBase* grid, size_t timeStepIndex )
|
||||
{
|
||||
if ( !isEvaluatable() ) return;
|
||||
m_sourceFilter()->applyToCellVisibility( cellVisibility, grid, timeStepIndex );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Toggling the wrapper's checkbox affects only the owning view; trigger regen there.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
||||
{
|
||||
if ( changedField == objectToggleField() )
|
||||
{
|
||||
if ( auto* view = firstAncestorOrThisOfType<Rim3dView>() )
|
||||
{
|
||||
view->scheduleGeometryRegen( PROPERTY_FILTERED );
|
||||
view->scheduleCreateDisplayModelAndRedraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
||||
{
|
||||
uiOrdering.add( nameField() );
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
|
||||
{
|
||||
uiTreeOrdering.skipRemainingChildren( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::initAfterRead()
|
||||
{
|
||||
syncNameFromSource();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInView::syncNameFromSource()
|
||||
{
|
||||
if ( m_sourceFilter() ) setName( m_sourceFilter()->name() );
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RimCheckableNamedObject.h"
|
||||
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
#include "cvfArray.h"
|
||||
|
||||
class RigGridBase;
|
||||
class RimCellFilter;
|
||||
|
||||
//==================================================================================================
|
||||
/// Per-view delegate for a case-level RimCellFilter. Exposes only an on/off toggle (inherited
|
||||
/// isChecked from RimCheckableNamedObject) and forwards applyToCellVisibility to the wrapped
|
||||
/// source filter. Toggling the wrapper schedules regen on the owning view only.
|
||||
//==================================================================================================
|
||||
class RimDataFilterInView : public RimCheckableNamedObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimDataFilterInView();
|
||||
~RimDataFilterInView() override;
|
||||
|
||||
RimCellFilter* sourceFilter() const;
|
||||
void setSourceFilter( RimCellFilter* sourceFilter );
|
||||
|
||||
bool isEvaluatable() const;
|
||||
|
||||
void applyToCellVisibility( cvf::UByteArray* cellVisibility, const RigGridBase* grid, size_t timeStepIndex );
|
||||
|
||||
protected:
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
||||
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
|
||||
void initAfterRead() override;
|
||||
|
||||
private:
|
||||
void syncNameFromSource();
|
||||
|
||||
caf::PdmPtrField<RimCellFilter*> m_sourceFilter;
|
||||
};
|
||||
@@ -0,0 +1,240 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimDataFilterInViewCollection.h"
|
||||
|
||||
#include "Rim3dView.h"
|
||||
#include "RimCellFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
#include "RimDataFilterInView.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEclipseView.h"
|
||||
|
||||
#include "cafPdmUiTreeOrdering.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT( RimDataFilterInViewCollection, "DataFilterInViewCollection" );
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterInViewCollection::RimDataFilterInViewCollection()
|
||||
{
|
||||
CAF_PDM_InitObject( "Data Filters", ":/CellFilter.png" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_wrappers, "Wrappers", "Filters" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_sourceCollection, "SourceCollection", "Source Collection" );
|
||||
m_sourceCollection.uiCapability()->setUiHidden( true );
|
||||
|
||||
setName( "Data Filters" );
|
||||
nameField()->uiCapability()->setUiHidden( true );
|
||||
|
||||
setCheckState( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterInViewCollection::~RimDataFilterInViewCollection() = default;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::setSourceCollection( RimDataFilterCollection* sourceCollection )
|
||||
{
|
||||
if ( m_sourceCollection() == sourceCollection ) return;
|
||||
|
||||
m_sourceCollection = sourceCollection;
|
||||
connectSourceSignal();
|
||||
syncWithSource();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterCollection* RimDataFilterInViewCollection::sourceCollection() const
|
||||
{
|
||||
return m_sourceCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Reconcile wrappers with the source. Mirrors RimSurfaceInViewCollection::syncSurfacesWithView.
|
||||
/// Step 1: drop wrappers whose source pointer no longer resolves (source filter was deleted).
|
||||
/// Step 2: rebuild ordered wrapper list following the source order; reuse existing wrappers
|
||||
/// (preserves their isChecked state); create new ones for previously-unseen sources.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::syncWithSource()
|
||||
{
|
||||
std::vector<RimDataFilterInView*> existing = m_wrappers.childrenByType();
|
||||
for ( auto* w : existing )
|
||||
{
|
||||
if ( !w->sourceFilter() )
|
||||
{
|
||||
m_wrappers.removeChild( w );
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_sourceCollection() )
|
||||
{
|
||||
m_wrappers.clearWithoutDelete();
|
||||
for ( auto* w : existing )
|
||||
delete w;
|
||||
updateConnectedEditors();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<RimDataFilterInView*> ordered;
|
||||
for ( RimCellFilter* src : m_sourceCollection()->filters() )
|
||||
{
|
||||
if ( !src ) continue;
|
||||
|
||||
RimDataFilterInView* wrapper = findWrapperFor( src );
|
||||
if ( !wrapper )
|
||||
{
|
||||
wrapper = new RimDataFilterInView();
|
||||
wrapper->setSourceFilter( src );
|
||||
}
|
||||
ordered.push_back( wrapper );
|
||||
}
|
||||
|
||||
m_wrappers.clearWithoutDelete();
|
||||
for ( auto* w : ordered )
|
||||
{
|
||||
m_wrappers.push_back( w );
|
||||
}
|
||||
updateConnectedEditors();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimDataFilterInView*> RimDataFilterInViewCollection::wrappers() const
|
||||
{
|
||||
return m_wrappers.childrenByType();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimDataFilterInView*> RimDataFilterInViewCollection::activeWrappers() const
|
||||
{
|
||||
std::vector<RimDataFilterInView*> active;
|
||||
if ( !isChecked() ) return active;
|
||||
|
||||
for ( RimDataFilterInView* w : m_wrappers )
|
||||
{
|
||||
if ( w && w->isEvaluatable() ) active.push_back( w );
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimDataFilterInViewCollection::hasActiveFilters() const
|
||||
{
|
||||
return !activeWrappers().empty();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::initAfterRead()
|
||||
{
|
||||
// Old project files (predating this branch) don't persist a SourceCollection reference, and
|
||||
// setSourceCollection() is normally invoked from RimEclipseView::propagateEclipseCaseToChildObjects
|
||||
// — which only runs when the case is assigned via the setter, not on plain XML load. Resolve the
|
||||
// case-level data filter collection through the PDM ancestry so wrappers always sync on load.
|
||||
if ( !m_sourceCollection() )
|
||||
{
|
||||
if ( auto* view = firstAncestorOrThisOfType<RimEclipseView>() )
|
||||
{
|
||||
if ( auto* eclCase = view->eclipseCase() ) m_sourceCollection = eclCase->dataFilterCollection();
|
||||
}
|
||||
}
|
||||
|
||||
connectSourceSignal();
|
||||
syncWithSource();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
|
||||
{
|
||||
for ( RimDataFilterInView* w : m_wrappers )
|
||||
{
|
||||
if ( w ) uiTreeOrdering.add( w );
|
||||
}
|
||||
uiTreeOrdering.skipRemainingChildren( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
||||
{
|
||||
if ( changedField == objectToggleField() )
|
||||
{
|
||||
scheduleViewRegen();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterInView* RimDataFilterInViewCollection::findWrapperFor( RimCellFilter* sourceFilter ) const
|
||||
{
|
||||
if ( !sourceFilter ) return nullptr;
|
||||
for ( RimDataFilterInView* w : m_wrappers )
|
||||
{
|
||||
if ( w && w->sourceFilter() == sourceFilter ) return w;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::connectSourceSignal()
|
||||
{
|
||||
if ( !m_sourceCollection() ) return;
|
||||
m_sourceCollection()->filtersChanged.connect( this, &RimDataFilterInViewCollection::onSourceFiltersChanged );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::onSourceFiltersChanged( const caf::SignalEmitter* /*emitter*/ )
|
||||
{
|
||||
syncWithSource();
|
||||
scheduleViewRegen();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDataFilterInViewCollection::scheduleViewRegen()
|
||||
{
|
||||
if ( auto* view = firstAncestorOrThisOfType<Rim3dView>() )
|
||||
{
|
||||
view->scheduleGeometryRegen( PROPERTY_FILTERED );
|
||||
view->scheduleCreateDisplayModelAndRedraw();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RimCheckableNamedObject.h"
|
||||
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
class RimCellFilter;
|
||||
class RimDataFilterCollection;
|
||||
class RimDataFilterInView;
|
||||
|
||||
//==================================================================================================
|
||||
/// View-level collection of RimDataFilterInView wrappers, one per case-level filter. Auto-syncs
|
||||
/// with the case-level RimDataFilterCollection (mirroring RimSurfaceInViewCollection's pattern):
|
||||
/// new wrappers default to checked, removed source filters drop their wrappers, and the wrapper
|
||||
/// list follows the case order.
|
||||
//==================================================================================================
|
||||
class RimDataFilterInViewCollection : public RimCheckableNamedObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimDataFilterInViewCollection();
|
||||
~RimDataFilterInViewCollection() override;
|
||||
|
||||
void setSourceCollection( RimDataFilterCollection* sourceCollection );
|
||||
RimDataFilterCollection* sourceCollection() const;
|
||||
|
||||
void syncWithSource();
|
||||
|
||||
std::vector<RimDataFilterInView*> wrappers() const;
|
||||
std::vector<RimDataFilterInView*> activeWrappers() const;
|
||||
|
||||
bool hasActiveFilters() const;
|
||||
|
||||
protected:
|
||||
void initAfterRead() override;
|
||||
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
|
||||
private:
|
||||
RimDataFilterInView* findWrapperFor( RimCellFilter* sourceFilter ) const;
|
||||
void connectSourceSignal();
|
||||
void onSourceFiltersChanged( const caf::SignalEmitter* emitter );
|
||||
void scheduleViewRegen();
|
||||
|
||||
caf::PdmChildArrayField<RimDataFilterInView*> m_wrappers;
|
||||
caf::PdmPtrField<RimDataFilterCollection*> m_sourceCollection;
|
||||
};
|
||||
@@ -175,6 +175,8 @@ void RimEclipsePropertyFilter::setIsDuplicatedFromLinkedView( bool isDuplicated
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEclipsePropertyFilter::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
||||
{
|
||||
RimCellFilter::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( &m_lowerBound == changedField || &m_integerLowerBound == changedField )
|
||||
{
|
||||
if ( m_lowerBound > m_upperBound ) m_upperBound = m_lowerBound;
|
||||
@@ -197,16 +199,32 @@ void RimEclipsePropertyFilter::fieldChangedByUi( const caf::PdmFieldHandle* chan
|
||||
updateIconState();
|
||||
uiCapability()->updateConnectedEditors();
|
||||
|
||||
parentContainer()->updateDisplayModelNotifyManagedViews( this );
|
||||
if ( auto* container = parentContainer() )
|
||||
container->updateDisplayModelNotifyManagedViews( this );
|
||||
else
|
||||
triggerFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// May return null when this filter lives in a case-level RimDataFilterCollection rather than a
|
||||
/// per-view RimEclipsePropertyFilterCollection. Callers must handle that case.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEclipsePropertyFilterCollection* RimEclipsePropertyFilter::parentContainer()
|
||||
{
|
||||
return firstAncestorOrThisOfType<RimEclipsePropertyFilterCollection>();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimEclipsePropertyFilterCollection* RimEclipsePropertyFilter::parentContainer()
|
||||
void RimEclipsePropertyFilter::setCase( RimCase* srcCase )
|
||||
{
|
||||
return firstAncestorOrThisOfTypeAsserted<RimEclipsePropertyFilterCollection>();
|
||||
RimCellFilter::setCase( srcCase );
|
||||
if ( auto* eclipseCase = dynamic_cast<RimEclipseCase*>( srcCase ) )
|
||||
{
|
||||
m_resultDefinition->setEclipseCase( eclipseCase );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -214,8 +232,6 @@ RimEclipsePropertyFilterCollection* RimEclipsePropertyFilter::parentContainer()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEclipsePropertyFilter::setToDefaultValues()
|
||||
{
|
||||
CVF_ASSERT( parentContainer() );
|
||||
|
||||
computeResultValueRange();
|
||||
|
||||
m_lowerBound.setValueWithFieldChanged( m_minimumResultValue );
|
||||
@@ -352,17 +368,12 @@ void RimEclipsePropertyFilter::updateRangeLabel()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEclipsePropertyFilter::isPropertyFilterControlled()
|
||||
{
|
||||
auto rimView = firstAncestorOrThisOfTypeAsserted<Rim3dView>();
|
||||
|
||||
bool isPropertyFilterControlled = false;
|
||||
// Case-level data filters have no view ancestor; treat as not controlled.
|
||||
auto rimView = firstAncestorOrThisOfType<Rim3dView>();
|
||||
if ( !rimView ) return false;
|
||||
|
||||
RimViewController* vc = rimView->viewController();
|
||||
if ( vc && vc->isPropertyFilterOveridden() )
|
||||
{
|
||||
isPropertyFilterControlled = true;
|
||||
}
|
||||
|
||||
return isPropertyFilterControlled;
|
||||
return vc && vc->isPropertyFilterOveridden();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -433,7 +444,8 @@ void RimEclipsePropertyFilter::defineObjectEditorAttribute( QString uiConfigName
|
||||
{
|
||||
if ( !m_isDuplicatedFromLinkedView ) return;
|
||||
|
||||
auto rimView = firstAncestorOrThisOfTypeAsserted<Rim3dView>();
|
||||
auto rimView = firstAncestorOrThisOfType<Rim3dView>();
|
||||
if ( !rimView ) return;
|
||||
|
||||
RimViewController* vc = rimView->viewController();
|
||||
if ( vc && vc->isPropertyFilterDuplicationActive() )
|
||||
@@ -487,8 +499,6 @@ void RimEclipsePropertyFilter::setLowerBound( const int& lowerBound )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimEclipsePropertyFilter::computeResultValueRange()
|
||||
{
|
||||
CVF_ASSERT( parentContainer() );
|
||||
|
||||
double min = HUGE_VAL;
|
||||
double max = -HUGE_VAL;
|
||||
|
||||
@@ -529,12 +539,11 @@ void RimEclipsePropertyFilter::computeResultValueRange()
|
||||
{
|
||||
if ( m_resultDefinition->resultType() == RiaDefines::ResultCatType::FORMATION_NAMES )
|
||||
{
|
||||
CVF_ASSERT( parentContainer()->reservoirView()->eclipseCase()->eclipseCaseData() );
|
||||
|
||||
const std::vector<QString> fnVector =
|
||||
parentContainer()->reservoirView()->eclipseCase()->eclipseCaseData()->formationNames();
|
||||
|
||||
setCategoryNames( fnVector );
|
||||
RimEclipseCase* ec = m_resultDefinition->eclipseCase();
|
||||
if ( ec && ec->eclipseCaseData() )
|
||||
{
|
||||
setCategoryNames( ec->eclipseCaseData()->formationNames() );
|
||||
}
|
||||
}
|
||||
else if ( m_resultDefinition->resultVariable() == RiaResultNames::completionTypeResultName() )
|
||||
{
|
||||
@@ -697,7 +706,14 @@ void RimEclipsePropertyFilter::initAfterRead()
|
||||
{
|
||||
m_resultDefinition->initAfterRead();
|
||||
|
||||
m_resultDefinition->setEclipseCase( parentContainer()->reservoirView()->eclipseCase() );
|
||||
if ( auto* container = parentContainer() )
|
||||
{
|
||||
m_resultDefinition->setEclipseCase( container->reservoirView()->eclipseCase() );
|
||||
}
|
||||
else if ( auto* ec = eclipseCase() )
|
||||
{
|
||||
m_resultDefinition->setEclipseCase( ec );
|
||||
}
|
||||
updateIconState();
|
||||
}
|
||||
|
||||
@@ -715,11 +731,17 @@ void RimEclipsePropertyFilter::applyToCellVisibility( cvf::UByteArray* cellVisib
|
||||
|
||||
resultDefinition()->loadResult();
|
||||
|
||||
auto* container = parentContainer();
|
||||
if ( !container ) return;
|
||||
auto* view = container->reservoirView();
|
||||
if ( !view || !view->eclipseCase() ) return;
|
||||
RigEclipseCaseData* eclipseCase = view->eclipseCase()->eclipseCaseData();
|
||||
// The result definition carries its own eclipse case binding (set via setEclipseCase). Prefer
|
||||
// that, since case-level data filters have no view-side property-filter-collection ancestor.
|
||||
RimEclipseCase* ec = resultDefinition()->eclipseCase();
|
||||
if ( !ec )
|
||||
{
|
||||
auto* container = parentContainer();
|
||||
auto* view = container ? container->reservoirView() : nullptr;
|
||||
ec = view ? view->eclipseCase() : nullptr;
|
||||
}
|
||||
if ( !ec ) return;
|
||||
RigEclipseCaseData* eclipseCase = ec->eclipseCaseData();
|
||||
if ( !eclipseCase ) return;
|
||||
|
||||
cvf::ref<RigResultAccessor> resultAccessor =
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
|
||||
void setIsDuplicatedFromLinkedView( bool isControlled );
|
||||
|
||||
void setCase( RimCase* srcCase ) override;
|
||||
|
||||
void setToDefaultValues();
|
||||
void updateFilterName();
|
||||
void computeResultValueRange();
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "Rim2dIntersectionViewCollection.h"
|
||||
#include "RimCaseCollection.h"
|
||||
#include "RimCellEdgeColors.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
#include "RimEclipseCellColors.h"
|
||||
#include "RimEclipseInputProperty.h"
|
||||
#include "RimEclipseInputPropertyCollection.h"
|
||||
@@ -124,6 +125,10 @@ RimEclipseCase::RimEclipseCase()
|
||||
CAF_PDM_InitFieldNoDefault( &m_viewCollection, "ViewCollection", "Views" );
|
||||
m_viewCollection = new RimEclipseViewCollection;
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_dataFilterCollection, "DataFilterCollection", "Data Filters" );
|
||||
m_dataFilterCollection = new RimDataFilterCollection;
|
||||
m_dataFilterCollection->setCase( this );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_wellTargetMappings, "WellTargetMappings", "Well Target Mappings" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_resultAliasList, "ResultAliasNames", "Result Name Aliases" );
|
||||
@@ -151,6 +156,7 @@ RimEclipseCase::~RimEclipseCase()
|
||||
delete m_fractureModelResults();
|
||||
delete m_inputPropertyCollection;
|
||||
delete m_viewCollection;
|
||||
delete m_dataFilterCollection;
|
||||
|
||||
RimProject* project = RimProject::current();
|
||||
if ( project )
|
||||
@@ -303,6 +309,8 @@ void RimEclipseCase::initAfterRead()
|
||||
}
|
||||
|
||||
m_contourMapCollection_OBSOLETE->clearWithoutDelete();
|
||||
|
||||
m_dataFilterCollection()->setCase( this );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -667,6 +675,8 @@ void RimEclipseCase::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrderin
|
||||
uiTreeOrdering.add( &m_2dIntersectionViewCollection );
|
||||
}
|
||||
|
||||
uiTreeOrdering.add( m_dataFilterCollection() );
|
||||
|
||||
uiTreeOrdering.add( &m_wellTargetMappings );
|
||||
}
|
||||
else if ( uiConfigName == "MainWindow.DataSources" )
|
||||
@@ -783,6 +793,14 @@ RimEclipseInputPropertyCollection* RimEclipseCase::inputPropertyCollection() con
|
||||
return m_inputPropertyCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterCollection* RimEclipseCase::dataFilterCollection() const
|
||||
{
|
||||
return m_dataFilterCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -48,6 +48,7 @@ class RigGridBase;
|
||||
class RigMainGrid;
|
||||
class RigVirtualPerforationTransmissibilities;
|
||||
class RimCaseCollection;
|
||||
class RimDataFilterCollection;
|
||||
class RimEclipseContourMapView;
|
||||
class RimEclipseContourMapViewCollection;
|
||||
class RimEclipseInputPropertyCollection;
|
||||
@@ -109,6 +110,7 @@ public:
|
||||
RimCaseCollection* parentCaseCollection();
|
||||
RimEclipseContourMapViewCollection* contourMapCollection() const;
|
||||
RimEclipseInputPropertyCollection* inputPropertyCollection() const;
|
||||
RimDataFilterCollection* dataFilterCollection() const;
|
||||
|
||||
QStringList timeStepStrings() const override;
|
||||
QString timeStepName( int frameIdx ) const override;
|
||||
@@ -191,6 +193,7 @@ private:
|
||||
caf::PdmChildField<RimReservoirCellResultsStorage*> m_matrixModelResults;
|
||||
caf::PdmChildField<RimReservoirCellResultsStorage*> m_fractureModelResults;
|
||||
caf::PdmChildField<RimEclipseViewCollection*> m_viewCollection;
|
||||
caf::PdmChildField<RimDataFilterCollection*> m_dataFilterCollection;
|
||||
|
||||
caf::PdmChildArrayField<RimWellTargetMapping*> m_wellTargetMappings;
|
||||
|
||||
|
||||
@@ -772,9 +772,13 @@ QList<caf::PdmOptionItemInfo> RimEclipseResultDefinition::calculateValueOptions(
|
||||
{
|
||||
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
|
||||
|
||||
RimEclipseView* eclView = firstAncestorOrThisOfTypeAsserted<RimEclipseView>();
|
||||
|
||||
auto eclipseCase = eclView->eclipseCase();
|
||||
// Prefer the directly-bound eclipse case (covers case-level result definitions that
|
||||
// have no view ancestor). Fall back to the host view's case for backward compat.
|
||||
RimEclipseCase* eclipseCase = m_eclipseCase;
|
||||
if ( !eclipseCase )
|
||||
{
|
||||
if ( auto* eclView = firstAncestorOrThisOfType<RimEclipseView>() ) eclipseCase = eclView->eclipseCase();
|
||||
}
|
||||
if ( eclipseCase && eclipseCase->eclipseCaseData() && eclipseCase->eclipseCaseData()->mainGrid() )
|
||||
{
|
||||
RimProject* proj = RimProject::current();
|
||||
@@ -797,9 +801,11 @@ QList<caf::PdmOptionItemInfo> RimEclipseResultDefinition::calculateValueOptions(
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_timeLapseBaseTimestep )
|
||||
{
|
||||
RimEclipseView* eclView = firstAncestorOrThisOfTypeAsserted<RimEclipseView>();
|
||||
|
||||
RimEclipseCase* currentCase = eclView->eclipseCase();
|
||||
RimEclipseCase* currentCase = m_eclipseCase;
|
||||
if ( !currentCase )
|
||||
{
|
||||
if ( auto* eclView = firstAncestorOrThisOfType<RimEclipseView>() ) currentCase = eclView->eclipseCase();
|
||||
}
|
||||
|
||||
RimEclipseCase* baseCase = currentCase;
|
||||
if ( m_differenceCase )
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
#include "RimCameraPosition.h"
|
||||
#include "RimCellEdgeColors.h"
|
||||
#include "RimCellFilterCollection.h"
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
#include "RimDataFilterInView.h"
|
||||
#include "RimDataFilterInViewCollection.h"
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimEclipseCaseTools.h"
|
||||
#include "RimEclipseCellColors.h"
|
||||
@@ -207,6 +211,9 @@ RimEclipseView::RimEclipseView()
|
||||
CAF_PDM_InitFieldNoDefault( &m_propertyFilterCollection, "PropertyFilters", "Property Filters" );
|
||||
m_propertyFilterCollection = new RimEclipsePropertyFilterCollection();
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_dataFiltersInView, "DataFiltersInView", "Data Filters" );
|
||||
m_dataFiltersInView = new RimDataFilterInViewCollection();
|
||||
|
||||
// Visualization fields
|
||||
CAF_PDM_InitField( &m_showInactiveCells, "ShowInactiveCells", false, "Show Inactive Cells" );
|
||||
CAF_PDM_InitField( &m_showInvalidCells, "ShowInvalidCells", false, "Show Invalid Cells" );
|
||||
@@ -420,6 +427,11 @@ void RimEclipseView::propagateEclipseCaseToChildObjects()
|
||||
cellFilterCollection()->setCase( currentEclipseCase );
|
||||
m_streamlineCollection->setEclipseCase( currentEclipseCase );
|
||||
|
||||
if ( m_dataFiltersInView() )
|
||||
{
|
||||
m_dataFiltersInView()->setSourceCollection( currentEclipseCase ? currentEclipseCase->dataFilterCollection() : nullptr );
|
||||
}
|
||||
|
||||
// Update grids node
|
||||
std::vector<RimGridCollection*> gridColls = descendantsIncludingThisOfType<RimGridCollection>();
|
||||
for ( RimGridCollection* gridCollection : gridColls )
|
||||
@@ -619,7 +631,7 @@ void RimEclipseView::onCreateDisplayModel()
|
||||
if ( !( eclipseCase() && eclipseCase()->eclipseCaseData() ) ) return;
|
||||
|
||||
const bool cellFiltersActive = cellFilterCollection()->hasActiveFilters();
|
||||
const bool propertyFiltersActive = eclipsePropertyFilterCollection()->hasActiveFilters();
|
||||
const bool propertyFiltersActive = hasActivePropertyOrDataFilters();
|
||||
|
||||
// Define a vector containing time step indices to produce geometry for.
|
||||
// First entry in this vector is used to define the geometry only result mode with no results.
|
||||
@@ -903,7 +915,7 @@ void RimEclipseView::onUpdateDisplayModelForCurrentTimeStep()
|
||||
|
||||
onUpdateLegends(); // To make sure the scalar mappers are set up correctly
|
||||
|
||||
if ( intersectionCollection()->shouldApplyCellFiltersToIntersections() && eclipsePropertyFilterCollection()->hasActiveFilters() )
|
||||
if ( intersectionCollection()->shouldApplyCellFiltersToIntersections() && hasActivePropertyOrDataFilters() )
|
||||
{
|
||||
appendIntersectionsForCurrentTimeStep();
|
||||
}
|
||||
@@ -942,7 +954,7 @@ void RimEclipseView::updateVisibleGeometries()
|
||||
{
|
||||
if ( viewController() && viewController()->isVisibleCellsOveridden() ) return;
|
||||
|
||||
if ( eclipsePropertyFilterCollection()->hasActiveFilters() )
|
||||
if ( hasActivePropertyOrDataFilters() )
|
||||
{
|
||||
cvf::ref<cvf::ModelBasicList> frameParts = new cvf::ModelBasicList;
|
||||
frameParts->setName( "GridModel" );
|
||||
@@ -1048,7 +1060,7 @@ void RimEclipseView::updateVisibleCellColors()
|
||||
{
|
||||
geometriesToRecolor.push_back( OVERRIDDEN_CELL_VISIBILITY );
|
||||
}
|
||||
else if ( eclipsePropertyFilterCollection()->hasActiveFilters() )
|
||||
else if ( hasActivePropertyOrDataFilters() )
|
||||
{
|
||||
geometriesToRecolor.push_back( PROPERTY_FILTERED );
|
||||
geometriesToRecolor.push_back( PROPERTY_FILTERED_WELL_CELLS );
|
||||
@@ -2067,6 +2079,7 @@ void RimEclipseView::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrderin
|
||||
uiTreeOrdering.add( cellResult() );
|
||||
uiTreeOrdering.add( cellEdgeResult() );
|
||||
uiTreeOrdering.add( cellFilterCollection() );
|
||||
uiTreeOrdering.add( m_dataFiltersInView() );
|
||||
uiTreeOrdering.add( m_propertyFilterCollection() );
|
||||
|
||||
uiTreeOrdering.add( elementVectorResult() );
|
||||
@@ -2196,7 +2209,7 @@ bool RimEclipseView::isTimeStepDependentDataVisible() const
|
||||
{
|
||||
if ( cellResult()->hasDynamicResult() ) return true;
|
||||
|
||||
if ( eclipsePropertyFilterCollection()->hasActiveDynamicFilters() ) return true;
|
||||
if ( hasActiveDynamicPropertyOrDataFilters() ) return true;
|
||||
|
||||
if ( wellCollection()->hasVisibleWellPipes() ) return true;
|
||||
|
||||
@@ -2327,6 +2340,54 @@ const RimEclipsePropertyFilterCollection* RimEclipseView::eclipsePropertyFilterC
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDataFilterInViewCollection* RimEclipseView::dataFiltersInView() const
|
||||
{
|
||||
return m_dataFiltersInView();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEclipseView::hasActivePropertyOrDataFilters() const
|
||||
{
|
||||
if ( eclipsePropertyFilterCollection()->hasActiveFilters() ) return true;
|
||||
if ( m_dataFiltersInView() && m_dataFiltersInView()->hasActiveFilters() ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Used to invalidate per-time-step caches: true if any active filter (view-level or case-level)
|
||||
/// depends on a dynamic Eclipse result. Mirrors the logic in
|
||||
/// RimEclipsePropertyFilterCollection::hasActiveDynamicFilters and RimCombinedFilter's recursive
|
||||
/// helper, but extended to traverse case-level wrappers as well.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimEclipseView::hasActiveDynamicPropertyOrDataFilters() const
|
||||
{
|
||||
if ( eclipsePropertyFilterCollection()->hasActiveDynamicFilters() ) return true;
|
||||
|
||||
if ( m_dataFiltersInView() )
|
||||
{
|
||||
for ( RimDataFilterInView* wrapper : m_dataFiltersInView()->activeWrappers() )
|
||||
{
|
||||
RimCellFilter* src = wrapper ? wrapper->sourceFilter() : nullptr;
|
||||
if ( !src ) continue;
|
||||
|
||||
if ( auto* ep = dynamic_cast<RimEclipsePropertyFilter*>( src ) )
|
||||
{
|
||||
if ( ep->resultDefinition() && ep->resultDefinition()->hasDynamicResult() ) return true;
|
||||
}
|
||||
else if ( auto* comb = dynamic_cast<RimCombinedFilter*>( src ) )
|
||||
{
|
||||
if ( comb->hasActiveDynamicPropertyDescendant() ) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -47,6 +47,7 @@ class Rim3dOverlayInfoConfig;
|
||||
class RimCellEdgeColors;
|
||||
class RimCellRangeFilter;
|
||||
class RimCellRangeFilterCollection;
|
||||
class RimDataFilterInViewCollection;
|
||||
class RimEclipseCase;
|
||||
class RimEclipseCellColors;
|
||||
class RimEclipseFaultColors;
|
||||
@@ -127,6 +128,11 @@ public:
|
||||
const RimEclipsePropertyFilterCollection* eclipsePropertyFilterCollection() const;
|
||||
void setOverridePropertyFilterCollection( RimEclipsePropertyFilterCollection* pfc );
|
||||
|
||||
RimDataFilterInViewCollection* dataFiltersInView() const;
|
||||
|
||||
bool hasActivePropertyOrDataFilters() const;
|
||||
bool hasActiveDynamicPropertyOrDataFilters() const;
|
||||
|
||||
RigCaseCellResultsData* currentGridCellResults() const;
|
||||
const RigActiveCellInfo* currentActiveCellInfo() const;
|
||||
RimEclipseCellColors* currentFaultResultColors();
|
||||
@@ -267,6 +273,8 @@ private:
|
||||
caf::PdmChildField<RimEclipsePropertyFilterCollection*> m_propertyFilterCollection;
|
||||
caf::PdmPointer<RimEclipsePropertyFilterCollection> m_overridePropertyFilterCollection;
|
||||
|
||||
caf::PdmChildField<RimDataFilterInViewCollection*> m_dataFiltersInView;
|
||||
|
||||
caf::PdmPtrField<RimEclipseCase*> m_customEclipseCase_OBSOLETE;
|
||||
|
||||
cvf::ref<RivReservoirViewPartMgr> m_reservoirGridPartManager;
|
||||
|
||||
@@ -67,6 +67,7 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/Intersect-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifPerforationIntervalReader-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimWellPathCompletions-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimDataFilterCollection-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryCaseCollection-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifActiveCellsReader-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifCsvDataTableFormatter-Test.cpp
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2026 Equinor ASA
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
// ResInsight 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.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RimCellFilter.h"
|
||||
#include "RimCellRangeFilter.h"
|
||||
#include "RimCombinedFilter.h"
|
||||
#include "RimDataFilterCollection.h"
|
||||
|
||||
#include "cafSignal.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace
|
||||
{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Lightweight slot owner used to capture caf::Signal emissions in tests.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class SignalCounter : public caf::SignalObserver
|
||||
{
|
||||
public:
|
||||
int count = 0;
|
||||
void onSignal( const caf::SignalEmitter* /*emitter*/ ) { ++count; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, emptyCollectionHasNoActiveFilters )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
EXPECT_EQ( size_t{ 0 }, coll.count() );
|
||||
EXPECT_TRUE( coll.isEmpty() );
|
||||
EXPECT_TRUE( coll.filters().empty() );
|
||||
EXPECT_FALSE( coll.hasActiveFilters() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, addFilterIncrementsCount )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
|
||||
auto* a = new RimCellRangeFilter();
|
||||
coll.addFilter( a );
|
||||
EXPECT_EQ( size_t{ 1 }, coll.count() );
|
||||
ASSERT_EQ( size_t{ 1 }, coll.filters().size() );
|
||||
EXPECT_EQ( a, coll.filters().front() );
|
||||
|
||||
auto* b = new RimCellRangeFilter();
|
||||
coll.addFilter( b );
|
||||
EXPECT_EQ( size_t{ 2 }, coll.count() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, removeFilterDeletesAndDecrements )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
|
||||
auto* a = new RimCellRangeFilter();
|
||||
coll.addFilter( a );
|
||||
EXPECT_EQ( size_t{ 1 }, coll.count() );
|
||||
|
||||
coll.removeFilter( a );
|
||||
EXPECT_EQ( size_t{ 0 }, coll.count() );
|
||||
EXPECT_TRUE( coll.filters().empty() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, addAndRemoveEmitFiltersChangedSignal )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
SignalCounter counter;
|
||||
coll.filtersChanged.connect( &counter, &SignalCounter::onSignal );
|
||||
|
||||
auto* a = new RimCellRangeFilter();
|
||||
coll.addFilter( a );
|
||||
EXPECT_EQ( 1, counter.count );
|
||||
|
||||
coll.removeFilter( a );
|
||||
EXPECT_EQ( 2, counter.count );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, childFilterChangeForwardsSignal )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
SignalCounter counter;
|
||||
coll.filtersChanged.connect( &counter, &SignalCounter::onSignal );
|
||||
|
||||
auto* a = new RimCellRangeFilter();
|
||||
coll.addFilter( a );
|
||||
const int afterAdd = counter.count;
|
||||
|
||||
a->triggerFilterChanged();
|
||||
EXPECT_EQ( afterAdd + 1, counter.count );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, hasActiveFiltersReflectsChildState )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
|
||||
auto* a = new RimCellRangeFilter();
|
||||
auto* b = new RimCellRangeFilter();
|
||||
coll.addFilter( a );
|
||||
coll.addFilter( b );
|
||||
|
||||
// Default-constructed RimCellFilter has isActive == true.
|
||||
EXPECT_TRUE( coll.hasActiveFilters() );
|
||||
|
||||
a->setActive( false );
|
||||
b->setActive( false );
|
||||
EXPECT_FALSE( coll.hasActiveFilters() );
|
||||
|
||||
a->setActive( true );
|
||||
EXPECT_TRUE( coll.hasActiveFilters() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, addNewRangeFilterReturnsConfigured )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
|
||||
RimCellRangeFilter* rangeFilter = coll.addNewRangeFilter();
|
||||
ASSERT_NE( nullptr, rangeFilter );
|
||||
EXPECT_EQ( size_t{ 1 }, coll.count() );
|
||||
EXPECT_EQ( rangeFilter, coll.filters().front() );
|
||||
|
||||
// setDefaultValues(-1, -1) leaves IJK starts >= 1 (1-based Eclipse indexing).
|
||||
EXPECT_GE( rangeFilter->startIndexI(), 1 );
|
||||
EXPECT_GE( rangeFilter->startIndexJ(), 1 );
|
||||
EXPECT_GE( rangeFilter->startIndexK(), 1 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, addNewCombinedFilterReturnsEmpty )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
|
||||
RimCombinedFilter* combined = coll.addNewCombinedFilter();
|
||||
ASSERT_NE( nullptr, combined );
|
||||
EXPECT_EQ( size_t{ 1 }, coll.count() );
|
||||
EXPECT_TRUE( combined->filters().empty() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Adding a child to a combined filter that lives in the collection should bubble up the
|
||||
/// filterChanged signal as a collection-level filtersChanged emission.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, combinedFilterChildEmitsCollectionSignal )
|
||||
{
|
||||
RimDataFilterCollection coll;
|
||||
|
||||
RimCombinedFilter* combined = coll.addNewCombinedFilter();
|
||||
ASSERT_NE( nullptr, combined );
|
||||
|
||||
SignalCounter counter;
|
||||
coll.filtersChanged.connect( &counter, &SignalCounter::onSignal );
|
||||
|
||||
auto* child = combined->addNewFilter<RimCellRangeFilter>( []( RimCellRangeFilter* ) {} );
|
||||
ASSERT_NE( nullptr, child );
|
||||
|
||||
// RimCombinedFilter::addFilter calls triggerFilterChanged, so the combined filter's signal
|
||||
// fires; the collection has connected to that signal and re-emits filtersChanged.
|
||||
EXPECT_GE( counter.count, 1 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimDataFilterCollection, pdmRoundTripPreservesFilters )
|
||||
{
|
||||
QString xml;
|
||||
{
|
||||
RimDataFilterCollection original;
|
||||
original.addNewRangeFilter();
|
||||
original.addNewCombinedFilter();
|
||||
ASSERT_EQ( size_t{ 2 }, original.count() );
|
||||
|
||||
xml = original.writeObjectToXmlString();
|
||||
}
|
||||
|
||||
RimDataFilterCollection restored;
|
||||
restored.readObjectFromXmlString( xml, caf::PdmDefaultObjectFactory::instance() );
|
||||
restored.resolveReferencesRecursively();
|
||||
|
||||
EXPECT_EQ( size_t{ 2 }, restored.count() );
|
||||
|
||||
auto items = restored.filters();
|
||||
ASSERT_EQ( size_t{ 2 }, items.size() );
|
||||
EXPECT_NE( nullptr, dynamic_cast<RimCellRangeFilter*>( items[0] ) );
|
||||
EXPECT_NE( nullptr, dynamic_cast<RimCombinedFilter*>( items[1] ) );
|
||||
}
|
||||
Reference in new issue
Block a user