From 6e23037bb89cc797ccd964d9da2eda17e456d9b7 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Thu, 8 Jan 2026 19:05:27 +0100 Subject: [PATCH] Refactors PdmUiItemInfo and PdmOptionItemInfo Refactors PdmUiItem and moves PdmUiItemInfo and PdmOptionItemInfo classes. Adds attribute support as a replacement for defineEditorAttributes --- .../cafPdmUiCore/CMakeLists.txt | 4 + .../cafPdmUiCore/cafPdmOptionItemInfo.cpp | 155 ++++++++ .../cafPdmUiCore/cafPdmOptionItemInfo.h | 178 ++++++++++ .../cafPdmUiCore/cafPdmUiItem.cpp | 333 +++++++----------- .../cafPdmUiCore/cafPdmUiItem.h | 275 +++++---------- .../cafPdmUiCore/cafPdmUiItemInfo.cpp | 91 +++++ .../cafPdmUiCore/cafPdmUiItemInfo.h | 104 ++++++ .../cafPdmUiCore/cafPdmUiOrdering.cpp | 2 +- 8 files changed, 748 insertions(+), 394 deletions(-) create mode 100644 Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.cpp create mode 100644 Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.h create mode 100644 Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.cpp create mode 100644 Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.h diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/CMakeLists.txt b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/CMakeLists.txt index 314263bbf9..27549d5595 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/CMakeLists.txt +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/CMakeLists.txt @@ -28,6 +28,10 @@ set(PROJECT_FILES cafPdmUiFieldSpecialization.h cafPdmUiItem.cpp cafPdmUiItem.h + cafPdmUiItemInfo.cpp + cafPdmUiItemInfo.h + cafPdmOptionItemInfo.cpp + cafPdmOptionItemInfo.h cafPdmUiModelChangeDetector.cpp cafPdmUiModelChangeDetector.h cafPdmUiObjectEditorHandle.cpp diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.cpp b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.cpp new file mode 100644 index 0000000000..49e09fd964 --- /dev/null +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.cpp @@ -0,0 +1,155 @@ +//################################################################################################## +// +// 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 <> +// 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 <> +// for more details. +// +//################################################################################################## + +#include "cafPdmOptionItemInfo.h" + +#include "cafPdmObjectHandle.h" +#include "cafPdmPointer.h" + +namespace caf +{ +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +PdmOptionItemInfo::PdmOptionItemInfo( const QString& anOptionUiText, + const QVariant& aValue, + bool isReadOnly, + const IconProvider& anIcon ) + : m_optionUiText( anOptionUiText ) + , m_value( aValue ) + , m_isReadOnly( isReadOnly ) + , m_iconProvider( anIcon ) + , m_level( 0 ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +PdmOptionItemInfo::PdmOptionItemInfo( const QString& anOptionUiText, + caf::PdmObjectHandle* obj, + bool isReadOnly, + const IconProvider& anIcon ) + : m_optionUiText( anOptionUiText ) + , m_isReadOnly( isReadOnly ) + , m_iconProvider( anIcon ) + , m_level( 0 ) +{ + m_value = QVariant::fromValue( caf::PdmPointer( obj ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +PdmOptionItemInfo PdmOptionItemInfo::createHeader( const QString& anOptionUiText, bool isReadOnly, const IconProvider& anIcon ) +{ + PdmOptionItemInfo header( anOptionUiText, QVariant(), isReadOnly, anIcon ); + + return header; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void PdmOptionItemInfo::setLevel( int level ) +{ + m_level = level; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString PdmOptionItemInfo::optionUiText() const +{ + return m_optionUiText; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QVariant PdmOptionItemInfo::value() const +{ + return m_value; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool PdmOptionItemInfo::isReadOnly() const +{ + return m_isReadOnly; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool PdmOptionItemInfo::isHeading() const +{ + return !m_value.isValid(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr PdmOptionItemInfo::icon() const +{ + return m_iconProvider.icon(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +int PdmOptionItemInfo::level() const +{ + return m_level; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList PdmOptionItemInfo::extractUiTexts( const QList& optionList ) +{ + QStringList texts; + + for ( const auto& option : optionList ) + { + texts.push_back( option.optionUiText() ); + } + + return texts; +} + +} // End of namespace caf diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.h b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.h new file mode 100644 index 0000000000..59e751aa46 --- /dev/null +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmOptionItemInfo.h @@ -0,0 +1,178 @@ +//################################################################################################## +// +// 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 <> +// 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 <> +// for more details. +// +//################################################################################################## + +#pragma once + +#include "cafIconProvider.h" +#include "cafPdmUiFieldSpecialization.h" + +#include +#include +#include + +class QIcon; + +namespace caf +{ +class PdmObjectHandle; + +//================================================================================================== +/// Class to keep Ui information about an option /choice in a Combobox or similar. +//================================================================================================== + +class PdmOptionItemInfo +{ +public: + // Template pass-through for enum types, ensuring the T type gets cast to an int before storing in the QVariant + // Note the extra dummy parameter. This ensures compilation fails for non-enum types and these variants get removed + // due to SFINAE (https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) + template + PdmOptionItemInfo( const QString& anOptionUiText, + T aValue, + bool isReadOnly = false, + const IconProvider& anIcon = IconProvider(), + typename std::enable_if::value>::type* = 0 ) + : PdmOptionItemInfo( anOptionUiText, QVariant( static_cast( aValue ) ), isReadOnly, anIcon ) + { + } + + PdmOptionItemInfo( const QString& anOptionUiText, + const QVariant& aValue, + bool isReadOnly = false, + const IconProvider& anIcon = IconProvider() ); + + PdmOptionItemInfo( const QString& anOptionUiText, + caf::PdmObjectHandle* obj, + bool isReadOnly = false, + const IconProvider& anIcon = IconProvider() ); + + static PdmOptionItemInfo createHeader( const QString& anOptionUiText, + bool isReadOnly = false, + const IconProvider& anIcon = IconProvider() ); + + void setLevel( int level ); + + QString optionUiText() const; + QVariant value() const; + bool isReadOnly() const; + bool isHeading() const; + std::unique_ptr icon() const; + int level() const; + + // Static utility methods to handle QList of PdmOptionItemInfo + // Please regard as private to the PDM system + + static QStringList extractUiTexts( const QList& optionList ); + + template + static bool findValues( const QList& optionList, + QVariant fieldValue, + std::vector& foundIndexes ); + +private: + QString m_optionUiText; + QVariant m_value; + bool m_isReadOnly; + IconProvider m_iconProvider; + int m_level; +}; + +//-------------------------------------------------------------------------------------------------- +/// Finds the indexes into the optionList that the field value(s) corresponds to. +/// In the case where the field is some kind of array, several indexes might be returned +/// The returned bool is true if all the fieldValues were found. +//-------------------------------------------------------------------------------------------------- +template +bool PdmOptionItemInfo::findValues( const QList& optionList, + QVariant fieldValue, + std::vector& foundIndexes ) +{ + foundIndexes.clear(); + + // Find this fieldvalue in the optionlist if present + + // First handle lists/arrays of values + if ( fieldValue.metaType().id() == QMetaType::QVariantList ) + { + QList valuesSelectedInField = fieldValue.toList(); + + if ( !valuesSelectedInField.empty() ) + { + // Create a list to be able to remove items as they are matched with values + std::list> optionVariantAndIndexPairs; + + for ( int i = 0; i < optionList.size(); ++i ) + { + optionVariantAndIndexPairs.push_back( std::make_pair( optionList[i].value(), i ) ); + } + + for ( const auto& value : valuesSelectedInField ) + { + std::list>::iterator it; + for ( it = optionVariantAndIndexPairs.begin(); it != optionVariantAndIndexPairs.end(); ++it ) + { + if ( PdmUiFieldSpecialization::isDataElementEqual( value, it->first ) ) + { + foundIndexes.push_back( it->second ); + + // Assuming that one option is referenced only once, the option is erased. Then break + // out of the inner loop, as this operation can be costly for fields with many options and many + // values + + optionVariantAndIndexPairs.erase( it ); + break; + } + } + } + } + + return ( static_cast( valuesSelectedInField.size() ) <= foundIndexes.size() ); + } + else // Then handle single value fields + { + for ( unsigned int opIdx = 0; opIdx < static_cast( optionList.size() ); ++opIdx ) + { + if ( PdmUiFieldSpecialization::isDataElementEqual( optionList[opIdx].value(), fieldValue ) ) + { + foundIndexes.push_back( opIdx ); + break; + } + } + return ( !foundIndexes.empty() ); + } +} + +} // End of namespace caf diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.cpp b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.cpp index 53cf05144a..187e095ed0 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.cpp +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.cpp @@ -3,7 +3,7 @@ // 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 +// This library may be used under the terms of the GNU General Public License or // the GNU Lesser General Public License as follows: // // GNU General Public License Usage @@ -35,189 +35,15 @@ //################################################################################################## #include "cafPdmUiItem.h" +#include "cafPdmLogging.h" +#include "cafPdmOptionItemInfo.h" #include "cafPdmUiEditorHandle.h" +#include "cafPdmUiItemInfo.h" #include "cafPdmUiObjectEditorHandle.h" #include "cafUpdateEditorsScheduler.h" namespace caf { -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -PdmUiItemInfo::PdmUiItemInfo( const QString& uiName, - IconProvider iconProvider /*= IconProvider() */, - QString toolTip /*= ""*/, - QString whatsThis /*= ""*/, - QString extraDebugText /*= ""*/ ) - : m_uiName( uiName ) - , m_iconProvider( iconProvider ) - , m_toolTip( toolTip ) - , m_whatsThis( whatsThis ) - , m_extraDebugText( extraDebugText ) - , m_editorTypeName( "" ) - , m_isHidden( false ) - , m_isTreeHidden( false ) - , m_isTreeChildrenHidden( false ) - , m_isReadOnly( false ) - , m_labelAlignment( LEFT ) - , m_isCustomContextMenuEnabled( false ) - , m_notifyAllFieldsInMultiFieldChangedEvents( -1 ) -{ -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -PdmUiItemInfo::PdmUiItemInfo( const QString& uiName, - QString iconResourceLocation /*= ""*/, - QString toolTip /*= ""*/, - QString whatsThis /*= ""*/, - QString extraDebugText /*= ""*/ ) - : m_uiName( uiName ) - , m_iconProvider( iconResourceLocation ) - , m_toolTip( toolTip ) - , m_whatsThis( whatsThis ) - , m_extraDebugText( extraDebugText ) - , m_editorTypeName( "" ) - , m_isHidden( false ) - , m_isTreeHidden( false ) - , m_isTreeChildrenHidden( false ) - , m_isReadOnly( false ) - , m_labelAlignment( LEFT ) - , m_isCustomContextMenuEnabled( false ) - , m_notifyAllFieldsInMultiFieldChangedEvents( -1 ) -{ -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::unique_ptr PdmUiItemInfo::icon() const -{ - return m_iconProvider.icon(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -const IconProvider& PdmUiItemInfo::iconProvider() const -{ - return m_iconProvider; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -PdmOptionItemInfo::PdmOptionItemInfo( const QString& anOptionUiText, - const QVariant& aValue, - bool isReadOnly /* = false */, - const IconProvider& anIcon /* = IconProvider()*/ ) - : m_optionUiText( anOptionUiText ) - , m_value( aValue ) - , m_isReadOnly( isReadOnly ) - , m_iconProvider( anIcon ) - , m_level( 0 ) -{ -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -PdmOptionItemInfo::PdmOptionItemInfo( const QString& anOptionUiText, - caf::PdmObjectHandle* obj, - bool isReadOnly /*= false*/, - const IconProvider& anIcon /*= IconProvider()*/ ) - : m_optionUiText( anOptionUiText ) - , m_isReadOnly( isReadOnly ) - , m_iconProvider( anIcon ) - , m_level( 0 ) -{ - m_value = QVariant::fromValue( caf::PdmPointer( obj ) ); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -PdmOptionItemInfo PdmOptionItemInfo::createHeader( const QString& anOptionUiText, - bool isReadOnly /*= false*/, - const IconProvider& anIcon /*= IconProvider()*/ ) -{ - PdmOptionItemInfo header( anOptionUiText, QVariant(), isReadOnly, anIcon ); - - return header; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void PdmOptionItemInfo::setLevel( int level ) -{ - m_level = level; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -const QString PdmOptionItemInfo::optionUiText() const -{ - return m_optionUiText; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -const QVariant PdmOptionItemInfo::value() const -{ - return m_value; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool PdmOptionItemInfo::isReadOnly() const -{ - return m_isReadOnly; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -bool PdmOptionItemInfo::isHeading() const -{ - return !m_value.isValid(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::unique_ptr PdmOptionItemInfo::icon() const -{ - return m_iconProvider.icon(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -int PdmOptionItemInfo::level() const -{ - return m_level; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QStringList PdmOptionItemInfo::extractUiTexts( const QList& optionList ) -{ - QStringList texts; - - for ( const auto& option : optionList ) - { - texts.push_back( option.optionUiText() ); - } - - return texts; -} - //================================================================================================== /// PdmUiItem //================================================================================================== @@ -403,9 +229,9 @@ bool PdmUiItem::isUiHidden( const QString& uiConfigName ) const const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo && !( conInfo->m_isHidden == -1 ) ) return conInfo->m_isHidden; - if ( defInfo && !( defInfo->m_isHidden == -1 ) ) return defInfo->m_isHidden; - if ( sttInfo && !( sttInfo->m_isHidden == -1 ) ) return sttInfo->m_isHidden; + if ( conInfo && conInfo->m_isHidden.has_value() ) return conInfo->m_isHidden.value(); + if ( defInfo && defInfo->m_isHidden.has_value() ) return defInfo->m_isHidden.value(); + if ( sttInfo && sttInfo->m_isHidden.has_value() ) return sttInfo->m_isHidden.value(); return false; } @@ -427,9 +253,9 @@ bool PdmUiItem::isUiTreeHidden( const QString& uiConfigName ) const const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo && !( conInfo->m_isTreeHidden == -1 ) ) return conInfo->m_isTreeHidden; - if ( defInfo && !( defInfo->m_isTreeHidden == -1 ) ) return defInfo->m_isTreeHidden; - if ( sttInfo && !( sttInfo->m_isTreeHidden == -1 ) ) return sttInfo->m_isTreeHidden; + if ( conInfo && conInfo->m_isTreeHidden.has_value() ) return conInfo->m_isTreeHidden.value(); + if ( defInfo && defInfo->m_isTreeHidden.has_value() ) return defInfo->m_isTreeHidden.value(); + if ( sttInfo && sttInfo->m_isTreeHidden.has_value() ) return sttInfo->m_isTreeHidden.value(); return false; } @@ -451,9 +277,9 @@ bool PdmUiItem::isUiTreeChildrenHidden( const QString& uiConfigName ) const const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo && !( conInfo->m_isTreeChildrenHidden == -1 ) ) return conInfo->m_isTreeChildrenHidden; - if ( defInfo && !( defInfo->m_isTreeChildrenHidden == -1 ) ) return defInfo->m_isTreeChildrenHidden; - if ( sttInfo && !( sttInfo->m_isTreeChildrenHidden == -1 ) ) return sttInfo->m_isTreeChildrenHidden; + if ( conInfo && conInfo->m_isTreeChildrenHidden.has_value() ) return conInfo->m_isTreeChildrenHidden.value(); + if ( defInfo && defInfo->m_isTreeChildrenHidden.has_value() ) return defInfo->m_isTreeChildrenHidden.value(); + if ( sttInfo && sttInfo->m_isTreeChildrenHidden.has_value() ) return sttInfo->m_isTreeChildrenHidden.value(); return false; } @@ -475,9 +301,9 @@ bool PdmUiItem::isUiReadOnly( const QString& uiConfigName /*= ""*/ ) const const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo && !( conInfo->m_isReadOnly == -1 ) ) return conInfo->m_isReadOnly; - if ( defInfo && !( defInfo->m_isReadOnly == -1 ) ) return defInfo->m_isReadOnly; - if ( sttInfo && !( sttInfo->m_isReadOnly == -1 ) ) return sttInfo->m_isReadOnly; + if ( conInfo && conInfo->m_isReadOnly.has_value() ) return conInfo->m_isReadOnly.value(); + if ( defInfo && defInfo->m_isReadOnly.has_value() ) return defInfo->m_isReadOnly.value(); + if ( sttInfo && sttInfo->m_isReadOnly.has_value() ) return sttInfo->m_isReadOnly.value(); return false; } @@ -499,12 +325,12 @@ bool PdmUiItem::notifyAllFieldsInMultiFieldChangedEvents( const QString& uiConfi const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo && !( conInfo->m_notifyAllFieldsInMultiFieldChangedEvents == -1 ) ) - return conInfo->m_notifyAllFieldsInMultiFieldChangedEvents; - if ( defInfo && !( defInfo->m_notifyAllFieldsInMultiFieldChangedEvents == -1 ) ) - return defInfo->m_notifyAllFieldsInMultiFieldChangedEvents; - if ( sttInfo && !( sttInfo->m_notifyAllFieldsInMultiFieldChangedEvents == -1 ) ) - return sttInfo->m_notifyAllFieldsInMultiFieldChangedEvents; + if ( conInfo && conInfo->m_notifyAllFieldsInMultiFieldChangedEvents.has_value() ) + return conInfo->m_notifyAllFieldsInMultiFieldChangedEvents.value(); + if ( defInfo && defInfo->m_notifyAllFieldsInMultiFieldChangedEvents.has_value() ) + return defInfo->m_notifyAllFieldsInMultiFieldChangedEvents.value(); + if ( sttInfo && sttInfo->m_notifyAllFieldsInMultiFieldChangedEvents.has_value() ) + return sttInfo->m_notifyAllFieldsInMultiFieldChangedEvents.value(); return false; } @@ -576,25 +402,25 @@ bool PdmUiItem::isUiGroup() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -PdmUiItemInfo::LabelPosType PdmUiItem::uiLabelPosition( const QString& uiConfigName ) const +PdmUiItemInfo::LabelPosition PdmUiItem::uiLabelPosition( const QString& uiConfigName ) const { const PdmUiItemInfo* conInfo = configInfo( uiConfigName ); const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo ) return conInfo->m_labelAlignment; - if ( defInfo ) return defInfo->m_labelAlignment; - if ( sttInfo ) return sttInfo->m_labelAlignment; + if ( conInfo ) return conInfo->m_labelPosition; + if ( defInfo ) return defInfo->m_labelPosition; + if ( sttInfo ) return sttInfo->m_labelPosition; - return PdmUiItemInfo::LEFT; + return PdmUiItemInfo::LabelPosition::LEFT; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void PdmUiItem::setUiLabelPosition( PdmUiItemInfo::LabelPosType alignment, const QString& uiConfigName /*= ""*/ ) +void PdmUiItem::setUiLabelPosition( PdmUiItemInfo::LabelPosition position, const QString& uiConfigName /*= ""*/ ) { - m_configItemInfos[uiConfigName].m_labelAlignment = alignment; + m_configItemInfos[uiConfigName].m_labelPosition = position; } //-------------------------------------------------------------------------------------------------- @@ -606,9 +432,12 @@ bool PdmUiItem::isCustomContextMenuEnabled( const QString& uiConfigName /*= ""*/ const PdmUiItemInfo* defInfo = defaultInfo(); const PdmUiItemInfo* sttInfo = m_staticItemInfo; - if ( conInfo && ( conInfo->m_isCustomContextMenuEnabled != -1 ) ) return conInfo->m_isCustomContextMenuEnabled; - if ( defInfo && ( defInfo->m_isCustomContextMenuEnabled != -1 ) ) return defInfo->m_isCustomContextMenuEnabled; - if ( sttInfo && ( sttInfo->m_isCustomContextMenuEnabled != -1 ) ) return sttInfo->m_isCustomContextMenuEnabled; + if ( conInfo && conInfo->m_isCustomContextMenuEnabled.has_value() ) + return conInfo->m_isCustomContextMenuEnabled.value(); + if ( defInfo && defInfo->m_isCustomContextMenuEnabled.has_value() ) + return defInfo->m_isCustomContextMenuEnabled.value(); + if ( sttInfo && sttInfo->m_isCustomContextMenuEnabled.has_value() ) + return sttInfo->m_isCustomContextMenuEnabled.value(); return false; } @@ -773,4 +602,96 @@ void PdmUiItem::addFieldEditor( PdmUiEditorHandle* fieldView ) m_editors.insert( fieldView ); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QVariant PdmUiItem::attributeVariant( const QString& key, const QString& uiConfigName ) const +{ + // Check config-specific attributes first + if ( !uiConfigName.isEmpty() ) + { + auto configIt = m_attributeMaps.find( uiConfigName ); + if ( configIt != m_attributeMaps.end() ) + { + auto attrIt = configIt->second.find( key ); + if ( attrIt != configIt->second.end() ) + { + return attrIt->second; + } + } + } + + // Fall back to default config ("") + auto defaultIt = m_attributeMaps.find( "" ); + if ( defaultIt != m_attributeMaps.end() ) + { + auto attrIt = defaultIt->second.find( key ); + if ( attrIt != defaultIt->second.end() ) + { + return attrIt->second; + } + } + + return QVariant(); // Return invalid QVariant if not found +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::list PdmUiItem::attributeNames( const QString& uiConfigName ) const +{ + std::list result; + + // Start with default config attributes + auto defaultIt = m_attributeMaps.find( "" ); + if ( defaultIt != m_attributeMaps.end() ) + { + for ( const auto& [key, value] : defaultIt->second ) + { + result.push_back( key ); + } + } + + // Add config-specific attributes (avoid duplicates) + if ( !uiConfigName.isEmpty() ) + { + auto configIt = m_attributeMaps.find( uiConfigName ); + if ( configIt != m_attributeMaps.end() ) + { + for ( const auto& [key, value] : configIt->second ) + { + // Check if key already exists in result + if ( std::find( result.begin(), result.end(), key ) == result.end() ) + { + result.push_back( key ); + } + } + } + } + + return result; +} + +//-------------------------------------------------------------------------------------------------- +/// Validate that all attributes set on this item are in the supported set +/// Logs warnings for unsupported attributes +//-------------------------------------------------------------------------------------------------- +void PdmUiItem::validateAttributes( const QString& contextName, + const std::set& supportedAttributes, + const QString& uiConfigName ) const +{ + auto allAttributeNames = attributeNames( uiConfigName ); + for ( const auto& key : allAttributeNames ) + { + if ( supportedAttributes.find( key ) == supportedAttributes.end() ) + { + CAF_PDM_LOG_WARNING( + QString( "%1: Unsupported attribute '%2' set on field. Supported attributes are: %3" ) + .arg( contextName ) + .arg( key ) + .arg( QStringList( supportedAttributes.begin(), supportedAttributes.end() ).join( ", " ) ) ); + } + } +} + } // End of namespace caf diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.h b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.h index b1ccdbcb1f..b708a95660 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.h +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItem.h @@ -37,199 +37,22 @@ #pragma once #include "cafIconProvider.h" -#include "cafPdmUiFieldSpecialization.h" +#include "cafPdmOptionItemInfo.h" +#include "cafPdmUiItemInfo.h" +#include +#include +#include #include +#include #include +#include + namespace caf { -//================================================================================================== -/// Class to keep (principally static) gui presentation information -/// of a data structure item (field or object) used by PdmUiItem -//================================================================================================== - -class PdmUiItemInfo -{ -public: - enum LabelPosType - { - LEFT, - TOP, - HIDDEN - }; - - PdmUiItemInfo() - : m_editorTypeName( "" ) - , m_isHidden( -1 ) - , m_isTreeHidden( -1 ) - , m_isTreeChildrenHidden( -1 ) - , m_isReadOnly( -1 ) - , m_labelAlignment( LEFT ) - , m_isCustomContextMenuEnabled( -1 ) - , m_notifyAllFieldsInMultiFieldChangedEvents( -1 ) - { - } - - PdmUiItemInfo( const QString& uiName, - QString iconResourceLocation = "", - QString toolTip = "", - QString whatsThis = "", - QString extraDebugText = "" ); - - PdmUiItemInfo( const QString& uiName, - IconProvider iconProvider = IconProvider(), - QString toolTip = "", - QString whatsThis = "", - QString extraDebugText = "" ); - - std::unique_ptr icon() const; - const IconProvider& iconProvider() const; - -private: - friend class PdmUiItem; - QString m_uiName; - IconProvider m_iconProvider; - QColor m_contentTextColor; ///< Color of a fields value text. Invalid by default. An Invalid color is not used. - QString m_toolTip; - QString m_whatsThis; - QString m_extraDebugText; - QString m_editorTypeName; ///< Use this exact type of editor to edit this UiItem - QString m_3dEditorTypeName; ///< If set, use this editor type to edit this UiItem in 3D - int m_isHidden; ///< UiItem should be hidden. -1 means not set - int m_isTreeHidden; ///< UiItem should be hidden in tree. -1 means not set - int m_isTreeChildrenHidden; ///< Children of UiItem should be hidden. -1 means not set - int m_isReadOnly; ///< UiItem should be insensitive, or read only. -1 means not set. - LabelPosType m_labelAlignment; - int m_isCustomContextMenuEnabled; - int m_notifyAllFieldsInMultiFieldChangedEvents; -}; - -//================================================================================================== -/// Class to keep Ui information about an option /choice in a Combobox or similar. -//================================================================================================== - -class PdmOptionItemInfo -{ -public: - // Template pass-through for enum types, ensuring the T type gets cast to an int before storing in the QVariant - // Note the extra dummy parameter. This ensures compilation fails for non-enum types and these variants get removed - // due to SFINAE (https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) - template - PdmOptionItemInfo( const QString& anOptionUiText, - T aValue, - bool isReadOnly = false, - const IconProvider& anIcon = IconProvider(), - typename std::enable_if::value>::type* = 0 ) - : PdmOptionItemInfo( anOptionUiText, QVariant( static_cast( aValue ) ), isReadOnly, anIcon ) - { - } - PdmOptionItemInfo( const QString& anOptionUiText, - const QVariant& aValue, - bool isReadOnly = false, - const IconProvider& anIcon = IconProvider() ); - PdmOptionItemInfo( const QString& anOptionUiText, - caf::PdmObjectHandle* obj, - bool isReadOnly = false, - const IconProvider& anIcon = IconProvider() ); - - static PdmOptionItemInfo createHeader( const QString& anOptionUiText, - bool isReadOnly = false, - const IconProvider& anIcon = IconProvider() ); - - void setLevel( int level ); - - const QString optionUiText() const; - const QVariant value() const; - bool isReadOnly() const; - bool isHeading() const; - std::unique_ptr icon() const; - int level() const; - - // Static utility methods to handle QList of PdmOptionItemInfo - // Please regard as private to the PDM system - - static QStringList extractUiTexts( const QList& optionList ); - template - static bool findValues( const QList& optionList, - QVariant fieldValue, - std::vector& foundIndexes ); - -private: - QString m_optionUiText; - QVariant m_value; - bool m_isReadOnly; - IconProvider m_iconProvider; - int m_level; -}; - class PdmUiEditorHandle; -//-------------------------------------------------------------------------------------------------- -/// Finds the indexes into the optionList that the field value(s) corresponds to. -/// In the case where the field is some kind of array, several indexes might be returned -/// The returned bool is true if all the fieldValues were found. -//-------------------------------------------------------------------------------------------------- -template -bool PdmOptionItemInfo::findValues( const QList& optionList, - QVariant fieldValue, - std::vector& foundIndexes ) -{ - foundIndexes.clear(); - - // Find this fieldvalue in the optionlist if present - - // First handle lists/arrays of values - if ( fieldValue.metaType().id() == QMetaType::QVariantList ) - { - QList valuesSelectedInField = fieldValue.toList(); - - if ( !valuesSelectedInField.empty() ) - { - // Create a list to be able to remove items as they are matched with values - std::list> optionVariantAndIndexPairs; - - for ( int i = 0; i < optionList.size(); ++i ) - { - optionVariantAndIndexPairs.push_back( std::make_pair( optionList[i].value(), i ) ); - } - - for ( int i = 0; i < valuesSelectedInField.size(); ++i ) - { - std::list>::iterator it; - for ( it = optionVariantAndIndexPairs.begin(); it != optionVariantAndIndexPairs.end(); ++it ) - { - if ( PdmUiFieldSpecialization::isDataElementEqual( valuesSelectedInField[i], it->first ) ) - { - foundIndexes.push_back( it->second ); - - // Assuming that one option is referenced only once, the option is erased. Then break - // out of the inner loop, as this operation can be costly for fields with many options and many - // values - - optionVariantAndIndexPairs.erase( it ); - break; - } - } - } - } - - return ( static_cast( valuesSelectedInField.size() ) <= foundIndexes.size() ); - } - else // Then handle single value fields - { - for ( unsigned int opIdx = 0; opIdx < static_cast( optionList.size() ); ++opIdx ) - { - if ( PdmUiFieldSpecialization::isDataElementEqual( optionList[opIdx].value(), fieldValue ) ) - { - foundIndexes.push_back( opIdx ); - break; - } - } - return ( !foundIndexes.empty() ); - } -} - //================================================================================================== /// Base class for all datastructure items (fields or objects) to make them have information on /// how to display them in the GUI. All the information can have a static variant valid for all @@ -279,8 +102,8 @@ public: bool notifyAllFieldsInMultiFieldChangedEvents( const QString& uiConfigName = "" ) const; void setNotifyAllFieldsInMultiFieldChangedEvents( bool enable, const QString& uiConfigName = "" ); - PdmUiItemInfo::LabelPosType uiLabelPosition( const QString& uiConfigName = "" ) const; - void setUiLabelPosition( PdmUiItemInfo::LabelPosType alignment, const QString& uiConfigName = "" ); + PdmUiItemInfo::LabelPosition uiLabelPosition( const QString& uiConfigName = "" ) const; + void setUiLabelPosition( PdmUiItemInfo::LabelPosition position, const QString& uiConfigName = "" ); bool isCustomContextMenuEnabled( const QString& uiConfigName = "" ) const; void setCustomContextMenuEnabled( bool enableCustomContextMenu, const QString& uiConfigName = "" ); @@ -293,6 +116,78 @@ public: virtual bool isUiGroup() const; + // Map-based attribute system + + /// Sets an attribute value for this UI item. + /// + /// Attributes provide a flexible way to configure UI editor behavior without + /// modifying the editor classes themselves. Each attribute is stored as a key-value + /// pair and can be retrieved later by editors during configuration. + /// + /// String Type Handling: + /// --------------------- + /// The method automatically converts C-style strings to QString for Qt integration: + /// - String literals: "text" or char[N] arrays + /// - String pointers: const char* or char* + /// All other types are stored directly in QVariant. + /// + /// Usage Examples: + /// --------------- + /// @code + /// // String literal (automatically converted to QString) + /// field.uiCapability()->setAttribute("dateFormat", "yyyy-MM-dd"); + /// + /// // Lambda/function (stored in QVariant) + /// field.uiCapability()->setAttribute("callback", myCallbackFunction); + /// + /// // Numeric value (stored in QVariant) + /// field.uiCapability()->setAttribute("maxLength", 100); + /// + /// @endcode + template + void setAttribute( const QString& key, const T& value, const QString& uiConfigName = "" ) + { + // Decay type removes references, const, and volatile qualifiers + // This helps us identify the true underlying type + using DecayedT = std::decay_t; + + // Check if value is a C-style string pointer (const char* or char*) + constexpr bool isCStringPointer = std::is_same_v || std::is_same_v; + + // Check if value is a character array (string literal like "text" or char[75]) + constexpr bool isCharArray = std::is_array_v && std::is_same_v, char>; + + if constexpr ( isCStringPointer || isCharArray ) + { + // Convert C-style strings to QString for proper Qt string handling + m_attributeMaps[uiConfigName][key] = QString( value ); + } + else + { + // Store all other types (int, double, function pointers, etc.) in QVariant + // QVariant handles type-safe storage and retrieval of various types + m_attributeMaps[uiConfigName][key] = QVariant::fromValue( value ); + } + } + + template + std::optional attribute( const QString& key, const QString& uiConfigName = "" ) const + { + QVariant value = attributeVariant( key, uiConfigName ); + if ( value.isValid() && value.canConvert() ) + { + return value.value(); + } + return std::nullopt; + } + + std::list attributeNames( const QString& uiConfigName = "" ) const; + + // Helper function to validate attributes against a supported set + void validateAttributes( const QString& contextName, + const std::set& supportedAttributes, + const QString& uiConfigName = "" ) const; + /// Intended to be called when fields in an object has been changed void updateConnectedEditors() const; void scheduleUpdateConnectedEditors() const; @@ -324,6 +219,9 @@ public: // Pdm-Private only protected: std::set m_editors; +private: + QVariant attributeVariant( const QString& key, const QString& uiConfigName = "" ) const; + private: const PdmUiItemInfo* defaultInfo() const; const PdmUiItemInfo* configInfo( const QString& uiConfigName ) const; @@ -331,6 +229,9 @@ private: PdmUiItemInfo* m_staticItemInfo; std::map m_configItemInfos; + // Map-based attributes: uiConfigName -> (attributeKey -> value) + std::map> m_attributeMaps; + static bool sm_showExtraDebugText; }; diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.cpp b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.cpp new file mode 100644 index 0000000000..866075b495 --- /dev/null +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.cpp @@ -0,0 +1,91 @@ +//################################################################################################## +// +// 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 <> +// 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 <> +// for more details. +// +//################################################################################################## + +#include "cafPdmUiItemInfo.h" + +namespace caf +{ +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +PdmUiItemInfo::PdmUiItemInfo( const QString& uiName, + IconProvider iconProvider, + QString toolTip, + QString whatsThis, + QString extraDebugText ) + : m_uiName( uiName ) + , m_iconProvider( iconProvider ) + , m_toolTip( toolTip ) + , m_whatsThis( whatsThis ) + , m_extraDebugText( extraDebugText ) + , m_labelPosition( LabelPosition::LEFT ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +PdmUiItemInfo::PdmUiItemInfo( const QString& uiName, + QString iconResourceLocation, + QString toolTip, + QString whatsThis, + QString extraDebugText ) + : m_uiName( uiName ) + , m_iconProvider( iconResourceLocation ) + , m_toolTip( toolTip ) + , m_whatsThis( whatsThis ) + , m_extraDebugText( extraDebugText ) + , m_labelPosition( LabelPosition::LEFT ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::unique_ptr PdmUiItemInfo::icon() const +{ + return m_iconProvider.icon(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const IconProvider& PdmUiItemInfo::iconProvider() const +{ + return m_iconProvider; +} + +} // End of namespace caf diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.h b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.h new file mode 100644 index 0000000000..bc466dfcfa --- /dev/null +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiItemInfo.h @@ -0,0 +1,104 @@ +//################################################################################################## +// +// 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 <> +// 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 <> +// for more details. +// +//################################################################################################## + +#pragma once + +#include "cafIconProvider.h" + +#include +#include + +class QIcon; + +namespace caf +{ +//================================================================================================== +/// Class to keep (principally static) gui presentation information +/// of a data structure item (field or object) used by PdmUiItem +//================================================================================================== + +class PdmUiItemInfo +{ +public: + enum class LabelPosition + { + LEFT, + TOP, + HIDDEN + }; + + PdmUiItemInfo() + : m_editorTypeName( "" ) + , m_labelPosition( LabelPosition::LEFT ) + { + } + + PdmUiItemInfo( const QString& uiName, + QString iconResourceLocation = "", + QString toolTip = "", + QString whatsThis = "", + QString extraDebugText = "" ); + + PdmUiItemInfo( const QString& uiName, + IconProvider iconProvider = IconProvider(), + QString toolTip = "", + QString whatsThis = "", + QString extraDebugText = "" ); + + std::unique_ptr icon() const; + const IconProvider& iconProvider() const; + +private: + friend class PdmUiItem; + QString m_uiName; + IconProvider m_iconProvider; + QColor m_contentTextColor; + QString m_toolTip; + QString m_whatsThis; + QString m_extraDebugText; + QString m_editorTypeName; + QString m_3dEditorTypeName; + LabelPosition m_labelPosition; + + std::optional m_isHidden; + std::optional m_isTreeHidden; + std::optional m_isTreeChildrenHidden; + std::optional m_isReadOnly; + std::optional m_isCustomContextMenuEnabled; + std::optional m_notifyAllFieldsInMultiFieldChangedEvents; +}; + +} // End of namespace caf diff --git a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiOrdering.cpp b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiOrdering.cpp index 4e90625616..6fe912b620 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiOrdering.cpp +++ b/Fwk/AppFwk/cafProjectDataModel/cafPdmUiCore/cafPdmUiOrdering.cpp @@ -431,7 +431,7 @@ void PdmUiOrdering::nrOfColumnsRequiredForItem( const FieldAndLayout& fieldAndLa { *fieldColumnsRequired = 1; *labelColumnsRequired = 0; - if ( uiItem->uiLabelPosition() == PdmUiItemInfo::LEFT ) + if ( uiItem->uiLabelPosition() == PdmUiItemInfo::LabelPosition::LEFT ) { *labelColumnsRequired = 1; if ( layoutOption.leftLabelColumnSpan != MAX_COLUMN_SPAN )