Files
ResInsight/Fwk/AppFwk/cafUserInterface/cafPdmUiSliderEditor.cpp
T
Magne Sjaastad eb3b5f14b5 #14178 Fix crash in slider editor from re-entrant field write
The integer slider editor cross-updated the spin box and slider without blocking signals, so a single user interaction wrote to the PDM field twice. The first (nested) write could trigger a field-changed callback that rebuilt the property panel and destroyed the spin box internal line edit, after which the second write dereferenced the dangling line edit in QAbstractSpinBox::text() and crashed.

Block the other widget signals while programmatically syncing in slotSliderValueChanged and updateSliderPosition, restoring the previous blocked state so it nests safely with configureAndUpdateUi. Add null guards in updateSliderPosition and writeValueToField.
2026-06-10 08:46:14 +02:00

213 lines
7.7 KiB
C++

//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#include "cafPdmUiSliderEditor.h"
#include "cafFactory.h"
#include "cafPdmField.h"
#include "cafPdmLogging.h"
#include "cafPdmObject.h"
#include "cafPdmUiDefaultObjectEditor.h"
#include "cafPdmUiFieldEditorHandle.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
namespace caf
{
CAF_PDM_UI_FIELD_EDITOR_SOURCE_INIT( PdmUiSliderEditor );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiSliderEditor::configureAndUpdateUi( const QString& uiConfigName )
{
CAF_ASSERT( !m_spinBox.isNull() );
PdmUiFieldEditorHandle::updateLabelFromField( m_label, uiConfigName );
m_spinBox->setEnabled( !uiField()->isUiReadOnly( uiConfigName ) );
m_spinBox->setToolTip( uiField()->uiToolTip( uiConfigName ) );
m_slider->setEnabled( !uiField()->isUiReadOnly( uiConfigName ) );
m_slider->setToolTip( uiField()->uiToolTip( uiConfigName ) );
caf::PdmUiObjectHandle* uiObject = uiObj( uiField()->fieldHandle()->ownerObject() );
if ( uiObject )
{
uiObject->editorAttribute( uiField()->fieldHandle(), uiConfigName, &m_attributes );
}
// Override with map-based attributes if present (new system takes precedence)
if ( auto uiItem = uiField() )
{
if ( auto val = uiItem->attribute<int>( Keys::MINIMUM, uiConfigName ) )
{
m_attributes.m_minimum = val.value();
}
if ( auto val = uiItem->attribute<int>( Keys::MAXIMUM, uiConfigName ) )
{
m_attributes.m_maximum = val.value();
}
if ( auto val = uiItem->attribute<bool>( Keys::SHOW_SPIN_BOX, uiConfigName ) )
{
m_attributes.m_showSpinBox = val.value();
}
if ( auto val = uiItem->attribute<int>( Keys::STEP, uiConfigName ) )
{
m_attributes.m_step = val.value();
}
// Validate: warn about unsupported attributes
uiItem->validateAttributes( "PdmUiSliderEditor", SUPPORTED_ATTRIBUTES, uiConfigName );
}
{
m_spinBox->blockSignals( true );
m_spinBox->setMinimum( m_attributes.m_minimum );
m_spinBox->setMaximum( m_attributes.m_maximum );
m_spinBox->setSingleStep( m_attributes.m_step );
QString textValue = uiField()->uiValue().toString();
m_spinBox->setValue( textValue.toInt() );
m_spinBox->blockSignals( false );
m_spinBox->setVisible( m_attributes.m_showSpinBox );
}
{
m_slider->blockSignals( true );
m_slider->setRange( m_attributes.m_minimum, m_attributes.m_maximum );
m_slider->setSingleStep( m_attributes.m_step );
updateSliderPosition();
m_slider->blockSignals( false );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* PdmUiSliderEditor::createEditorWidget( QWidget* parent )
{
QWidget* containerWidget = new QWidget( parent );
QHBoxLayout* layout = new QHBoxLayout();
layout->setContentsMargins( 0, 0, 0, 0 );
containerWidget->setLayout( layout );
m_spinBox = new QSpinBox( containerWidget );
m_spinBox->setMaximumWidth( 60 );
m_spinBox->setKeyboardTracking( false );
connect( m_spinBox, SIGNAL( valueChanged( int ) ), this, SLOT( slotSpinBoxValueChanged( int ) ) );
m_slider = new QSlider( Qt::Horizontal, containerWidget );
layout->addWidget( m_spinBox );
layout->addWidget( m_slider );
connect( m_slider, SIGNAL( valueChanged( int ) ), this, SLOT( slotSliderValueChanged( int ) ) );
return containerWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiSliderEditor::slotSliderValueChanged( int position )
{
if ( m_spinBox )
{
// Block signals to avoid re-entering slotSpinBoxValueChanged(), which would write to the
// field a second time and may tear down this editor's widgets while we are still executing.
bool wasBlocked = m_spinBox->blockSignals( true );
m_spinBox->setValue( position );
m_spinBox->blockSignals( wasBlocked );
}
writeValueToField();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiSliderEditor::slotSpinBoxValueChanged( int spinBoxValue )
{
updateSliderPosition();
writeValueToField();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiSliderEditor::updateSliderPosition()
{
if ( m_spinBox.isNull() || m_slider.isNull() ) return;
QString textValue = m_spinBox->text();
bool convertOk = false;
int newSliderValue = textValue.toInt( &convertOk );
if ( convertOk )
{
newSliderValue = qBound( m_attributes.m_minimum, newSliderValue, m_attributes.m_maximum );
// Block signals to avoid re-entering slotSliderValueChanged(), which would write to the
// field a second time and may tear down this editor's widgets while we are still executing.
bool wasBlocked = m_slider->blockSignals( true );
m_slider->setValue( newSliderValue );
m_slider->blockSignals( wasBlocked );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmUiSliderEditor::writeValueToField()
{
if ( m_spinBox.isNull() ) return;
QString textValue = m_spinBox->text();
QVariant v;
v = textValue;
this->setValueToField( v );
}
} // end namespace caf