/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "FormattedField.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // needed as long as we use the SolarMutex #include #include #include #include #include using namespace dbtools; using namespace css::uno; using namespace css::sdb; using namespace css::sdbc; using namespace css::sdbcx; using namespace css::beans; using namespace css::container; using namespace css::form; using namespace css::awt; using namespace css::io; using namespace css::lang; using namespace css::util; using namespace css::form::binding; namespace frm { namespace { class StandardFormatsSupplier : public SvNumberFormatsSupplierObj, public ::utl::ITerminationListener { protected: std::unique_ptr m_pMyPrivateFormatter; static WeakReference< XNumberFormatsSupplier > s_xDefaultFormatsSupplier; public: static Reference< XNumberFormatsSupplier > get( const Reference< XComponentContext >& _rxORB ); protected: StandardFormatsSupplier(const Reference< XComponentContext >& _rxFactory,LanguageType _eSysLanguage); virtual ~StandardFormatsSupplier() override; protected: virtual bool queryTermination() const override; virtual void notifyTermination() override; }; } WeakReference< XNumberFormatsSupplier > StandardFormatsSupplier::s_xDefaultFormatsSupplier; StandardFormatsSupplier::StandardFormatsSupplier(const Reference< XComponentContext > & _rxContext,LanguageType _eSysLanguage) :m_pMyPrivateFormatter(new SvNumberFormatter(_rxContext, _eSysLanguage)) { SetNumberFormatter(m_pMyPrivateFormatter.get()); // #i29147# ::utl::DesktopTerminationObserver::registerTerminationListener( this ); } StandardFormatsSupplier::~StandardFormatsSupplier() { ::utl::DesktopTerminationObserver::revokeTerminationListener( this ); } Reference< XNumberFormatsSupplier > StandardFormatsSupplier::get( const Reference< XComponentContext >& _rxORB ) { LanguageType eSysLanguage = LANGUAGE_SYSTEM; { ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); Reference< XNumberFormatsSupplier > xSupplier = s_xDefaultFormatsSupplier; if ( xSupplier.is() ) return xSupplier; // get the Office's locale eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false); } rtl::Reference pSupplier = new StandardFormatsSupplier( _rxORB, eSysLanguage ); { ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); Reference< XNumberFormatsSupplier > xSupplier = s_xDefaultFormatsSupplier; if ( xSupplier.is() ) // somebody used the small time frame where the mutex was not locked to create and set // the supplier return xSupplier; s_xDefaultFormatsSupplier = css::uno::Reference(pSupplier); } return pSupplier; } bool StandardFormatsSupplier::queryTermination() const { return true; } void StandardFormatsSupplier::notifyTermination() { Reference< XNumberFormatsSupplier > xKeepAlive = this; // when the application is terminating, release our static reference so that we are cleared/destructed // earlier than upon unloading the library // #i29147# s_xDefaultFormatsSupplier = WeakReference< XNumberFormatsSupplier >( ); SetNumberFormatter( nullptr ); m_pMyPrivateFormatter.reset(); } Sequence OFormattedControl::_getTypes() { return ::comphelper::concatSequences( OFormattedControl_BASE::getTypes(), OBoundControl::_getTypes() ); } Any SAL_CALL OFormattedControl::queryAggregation(const Type& _rType) { Any aReturn = OBoundControl::queryAggregation(_rType); if (!aReturn.hasValue()) aReturn = OFormattedControl_BASE::queryInterface(_rType); return aReturn; } OFormattedControl::OFormattedControl(const Reference& _rxFactory) :OBoundControl(_rxFactory, VCL_CONTROL_FORMATTEDFIELD) ,m_nKeyEvent(nullptr) { osl_atomic_increment(&m_refCount); { Reference xComp; if (query_aggregation(m_xAggregate, xComp)) { xComp->addKeyListener(this); } } osl_atomic_decrement(&m_refCount); } OFormattedControl::~OFormattedControl() { if( m_nKeyEvent ) Application::RemoveUserEvent( m_nKeyEvent ); if (!OComponentHelper::rBHelper.bDisposed) { acquire(); dispose(); } } // XKeyListener void OFormattedControl::disposing(const EventObject& _rSource) { OBoundControl::disposing(_rSource); } void OFormattedControl::keyPressed(const css::awt::KeyEvent& e) { if( e.KeyCode != KEY_RETURN || e.Modifiers != 0 ) return; // Is the control located in a form with a Submit URL? Reference xSet(getModel(), UNO_QUERY); if( !xSet.is() ) return; Reference xFComp(xSet, UNO_QUERY); css::uno::Reference xParent = xFComp->getParent(); if( !xParent.is() ) return; Reference xFormSet(xParent, UNO_QUERY); if( !xFormSet.is() ) return; Any aTmp(xFormSet->getPropertyValue( PROPERTY_TARGET_URL )); if (!aTmp.has() || getString(aTmp).isEmpty() ) return; Reference xElements(xParent, UNO_QUERY); sal_Int32 nCount = xElements->getCount(); if( nCount > 1 ) { Reference xFCSet; for( sal_Int32 nIndex=0; nIndex < nCount; nIndex++ ) { // Any aElement(xElements->getByIndex(nIndex)); xElements->getByIndex(nIndex) >>= xFCSet; if (hasProperty(PROPERTY_CLASSID, xFCSet) && getINT16(xFCSet->getPropertyValue(PROPERTY_CLASSID)) == FormComponentType::TEXTFIELD) { // Found another Edit -> Do not submit then if (xFCSet != xSet) return; } } } // Because we're still in the Handler, execute submit asynchronously if( m_nKeyEvent ) Application::RemoveUserEvent( m_nKeyEvent ); m_nKeyEvent = Application::PostUserEvent( LINK(this, OFormattedControl, OnKeyPressed) ); } void OFormattedControl::keyReleased(const css::awt::KeyEvent& /*e*/) { } IMPL_LINK_NOARG(OFormattedControl, OnKeyPressed, void*, void) { m_nKeyEvent = nullptr; Reference xFComp(getModel(), UNO_QUERY); css::uno::Reference xParent = xFComp->getParent(); Reference xSubmit(xParent, UNO_QUERY); if (xSubmit.is()) xSubmit->submit( Reference (), css::awt::MouseEvent() ); } css::uno::Sequence OFormattedControl::getSupportedServiceNames() { css::uno::Sequence aSupported = OBoundControl::getSupportedServiceNames(); aSupported.realloc(aSupported.getLength() + 2); OUString*pArray = aSupported.getArray(); pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_FORMATTEDFIELD; pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD; return aSupported; } void OFormattedModel::implConstruct() { // members m_bOriginalNumeric = false; m_bNumeric = false; m_xOriginalFormatter = nullptr; m_nKeyType = NumberFormat::UNDEFINED; m_aNullDate = DBTypeConversion::getStandardDate(); // default our formats supplier osl_atomic_increment(&m_refCount); setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER); osl_atomic_decrement(&m_refCount); startAggregatePropertyListening( PROPERTY_FORMATKEY ); startAggregatePropertyListening( PROPERTY_FORMATSSUPPLIER ); } OFormattedModel::OFormattedModel(const Reference& _rxFactory) :OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_FORMATTEDFIELD, FRM_SUN_CONTROL_FORMATTEDFIELD, true, true ) // use the old control name for compytibility reasons ,OErrorBroadcaster( OComponentHelper::rBHelper ) { implConstruct(); m_nClassId = FormComponentType::TEXTFIELD; initValueProperty( PROPERTY_EFFECTIVE_VALUE, PROPERTY_ID_EFFECTIVE_VALUE ); } OFormattedModel::OFormattedModel( const OFormattedModel* _pOriginal, const Reference< XComponentContext >& _rxFactory ) :OEditBaseModel( _pOriginal, _rxFactory ) ,OErrorBroadcaster( OComponentHelper::rBHelper ) { implConstruct(); } OFormattedModel::~OFormattedModel() { } // XCloneable css::uno::Reference< css::util::XCloneable > SAL_CALL OFormattedModel::createClone() { rtl::Reference pClone = new OFormattedModel(this, getContext()); pClone->clonedFrom(this); return pClone; } void SAL_CALL OFormattedModel::disposing() { OErrorBroadcaster::disposing(); OEditBaseModel::disposing(); } // XServiceInfo css::uno::Sequence OFormattedModel::getSupportedServiceNames() { css::uno::Sequence aSupported = OEditBaseModel::getSupportedServiceNames(); sal_Int32 nOldLen = aSupported.getLength(); aSupported.realloc( nOldLen + 9 ); OUString* pStoreTo = aSupported.getArray() + nOldLen; *pStoreTo++ = BINDABLE_CONTROL_MODEL; *pStoreTo++ = DATA_AWARE_CONTROL_MODEL; *pStoreTo++ = VALIDATABLE_CONTROL_MODEL; *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL; *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL; *pStoreTo++ = FRM_SUN_COMPONENT_FORMATTEDFIELD; *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD; *pStoreTo++ = BINDABLE_DATABASE_FORMATTED_FIELD; *pStoreTo++ = FRM_COMPONENT_FORMATTEDFIELD; return aSupported; } // XAggregation Any SAL_CALL OFormattedModel::queryAggregation(const Type& _rType) { Any aReturn = OEditBaseModel::queryAggregation( _rType ); return aReturn.hasValue() ? aReturn : OErrorBroadcaster::queryInterface( _rType ); } // XTypeProvider Sequence< Type > OFormattedModel::_getTypes() { return ::comphelper::concatSequences( OEditBaseModel::_getTypes(), OErrorBroadcaster::getTypes() ); } // XPersistObject OUString SAL_CALL OFormattedModel::getServiceName() { return FRM_COMPONENT_EDIT; } // XPropertySet void OFormattedModel::describeFixedProperties( Sequence< Property >& _rProps ) const { OEditBaseModel::describeFixedProperties( _rProps ); sal_Int32 nOldCount = _rProps.getLength(); _rProps.realloc( nOldCount + 3); css::beans::Property* pProperties = _rProps.getArray() + nOldCount; *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT); DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?"); } void OFormattedModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const { OEditBaseModel::describeAggregateProperties( _rAggregateProps ); // TreatAsNumeric is not transient: we want to attach it to the UI // This is necessary to make EffectiveDefault (which may be text or a number) meaningful ModifyPropertyAttributes(_rAggregateProps, PROPERTY_TREATASNUMERIC, 0, PropertyAttribute::TRANSIENT); // Same for FormatKey // (though the paragraph above for the TreatAsNumeric does not hold anymore - we do not have an UI for this. // But we have for the format key ...) ModifyPropertyAttributes(_rAggregateProps, PROPERTY_FORMATKEY, 0, PropertyAttribute::TRANSIENT); RemoveProperty(_rAggregateProps, PROPERTY_STRICTFORMAT); // no strict format property for formatted fields: it does not make sense, 'cause // there is no general way to decide which characters/sub strings are allowed during the input of an // arbitrary formatted control } void OFormattedModel::setPropertyToDefaultByHandle(sal_Int32 nHandle) { if (nHandle == PROPERTY_ID_FORMATSSUPPLIER) { Reference xSupplier = calcDefaultFormatsSupplier(); DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::setPropertyToDefaultByHandle(FORMATSSUPPLIER) : have no aggregate !"); if (m_xAggregateSet.is()) m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier)); } else OEditBaseModel::setPropertyToDefaultByHandle(nHandle); } void OFormattedModel::setPropertyToDefault(const OUString& aPropertyName) { OPropertyArrayAggregationHelper& rPH = m_aPropertyBagHelper.getInfoHelper(); sal_Int32 nHandle = rPH.getHandleByName( aPropertyName ); if (nHandle == PROPERTY_ID_FORMATSSUPPLIER) setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER); else OEditBaseModel::setPropertyToDefault(aPropertyName); } Any OFormattedModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const { if (nHandle == PROPERTY_ID_FORMATSSUPPLIER) { Reference xSupplier = calcDefaultFormatsSupplier(); return Any(xSupplier); } else return OEditBaseModel::getPropertyDefaultByHandle(nHandle); } Any SAL_CALL OFormattedModel::getPropertyDefault( const OUString& aPropertyName ) { OPropertyArrayAggregationHelper& rPH = m_aPropertyBagHelper.getInfoHelper(); sal_Int32 nHandle = rPH.getHandleByName( aPropertyName ); if (nHandle == PROPERTY_ID_FORMATSSUPPLIER) return getPropertyDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER); else return OEditBaseModel::getPropertyDefault(aPropertyName); } void OFormattedModel::_propertyChanged( const css::beans::PropertyChangeEvent& evt ) { // TODO: check how this works with external bindings OSL_ENSURE( evt.Source == m_xAggregateSet, "OFormattedModel::_propertyChanged: where did this come from?" ); if ( evt.Source != m_xAggregateSet ) return; if ( evt.PropertyName == PROPERTY_FORMATKEY ) { if ( evt.NewValue.getValueType().getTypeClass() == TypeClass_LONG ) { try { ::osl::MutexGuard aGuard( m_aMutex ); Reference xSupplier( calcFormatsSupplier() ); m_nKeyType = getNumberFormatType(xSupplier->getNumberFormats(), getINT32( evt.NewValue ) ); // as m_aSaveValue (which is used by commitControlValueToDbColumn) is format dependent we have // to recalc it, which is done by translateDbColumnToControlValue if ( m_xColumn.is() && m_xAggregateFastSet.is() && !m_xCursor->isBeforeFirst() && !m_xCursor->isAfterLast()) { setControlValue( translateDbColumnToControlValue(), eOther ); } // if we're connected to an external value binding, then re-calculate the type // used to exchange the value - it depends on the format, too if ( hasExternalValueBinding() ) { calculateExternalValueType(); } } catch(const Exception&) { } } return; } if ( evt.PropertyName == PROPERTY_FORMATSSUPPLIER ) { updateFormatterNullDate(); return; } OBoundControlModel::_propertyChanged( evt ); } void OFormattedModel::updateFormatterNullDate() { // calc the current NULL date Reference< XNumberFormatsSupplier > xSupplier( calcFormatsSupplier() ); if ( xSupplier.is() ) xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate; } Reference< XNumberFormatsSupplier > OFormattedModel::calcFormatsSupplier() const { Reference xSupplier; DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::calcFormatsSupplier : have no aggregate !"); // Does my aggregate model have a FormatSupplier? if( m_xAggregateSet.is() ) m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xSupplier; if (!xSupplier.is()) // check if my parent form has a supplier xSupplier = calcFormFormatsSupplier(); if (!xSupplier.is()) xSupplier = calcDefaultFormatsSupplier(); DBG_ASSERT(xSupplier.is(), "OFormattedModel::calcFormatsSupplier : no supplier !"); // We should have one by now return xSupplier; } Reference OFormattedModel::calcFormFormatsSupplier() const { Reference xMe(const_cast(this)); // By this we make sure that we get the right object even when aggregating DBG_ASSERT(xMe.is(), "OFormattedModel::calcFormFormatsSupplier : I should have a content interface !"); // Iterate through until we reach a StartForm (starting with an own Parent) Reference xParent(xMe->getParent(), UNO_QUERY); Reference xNextParentForm(xParent, UNO_QUERY); while (!xNextParentForm.is() && xParent.is()) { xParent.set(xParent->getParent(), css::uno::UNO_QUERY); xNextParentForm.set(xParent, css::uno::UNO_QUERY); } if (!xNextParentForm.is()) { OSL_FAIL("OFormattedModel::calcFormFormatsSupplier : have no ancestor which is a form !"); return nullptr; } // The FormatSupplier of my ancestor (if it has one) Reference< XRowSet > xRowSet( xNextParentForm, UNO_QUERY ); Reference< XNumberFormatsSupplier > xSupplier; if (xRowSet.is()) xSupplier = getNumberFormats( getConnection(xRowSet), true, getContext() ); return xSupplier; } Reference< XNumberFormatsSupplier > OFormattedModel::calcDefaultFormatsSupplier() const { return StandardFormatsSupplier::get( getContext() ); } // XBoundComponent void OFormattedModel::loaded(const EventObject& rEvent) { // HACK: our onConnectedDbColumn accesses our NumberFormatter which locks the solar mutex (as it doesn't have // an own one). To prevent deadlocks with other threads which may request a property from us in an // UI-triggered action (e.g. a tooltip) we lock the solar mutex _here_ before our base class locks // its own mutex (which is used for property requests) // alternative a): we use two mutexes, one which is passed to the OPropertysetHelper and used for // property requests and one for our own code. This would need a lot of code rewriting // alternative b): The NumberFormatter has to be really threadsafe (with an own mutex), which is // the only "clean" solution for me. SolarMutexGuard aGuard; OEditBaseModel::loaded(rEvent); } void OFormattedModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm ) { m_xOriginalFormatter = nullptr; // get some properties of the field Reference xField = getField(); sal_Int32 nFormatKey = 0; DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::onConnectedDbColumn : have no aggregate !"); if (m_xAggregateSet.is()) { // all the following doesn't make any sense if we have no aggregate ... Any aSupplier = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER); DBG_ASSERT( aSupplier.hasValue(), "OFormattedModel::onConnectedDbColumn : invalid property value !" ); // This should've been set to the correct value in the ctor or in the read Any aFmtKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY); if ( !(aFmtKey >>= nFormatKey ) ) { // nobody gave us a format to use. So we examine the field we're bound to for a // format key, and use it ourself, too sal_Int32 nType = DataType::VARCHAR; if (xField.is()) { aFmtKey = xField->getPropertyValue(PROPERTY_FORMATKEY); xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nType ; } Reference xSupplier = calcFormFormatsSupplier(); DBG_ASSERT(xSupplier.is(), "OFormattedModel::onConnectedDbColumn : bound to a field but no parent with a formatter ? how this ?"); if (xSupplier.is()) { m_bOriginalNumeric = getBOOL(getPropertyValue(PROPERTY_TREATASNUMERIC)); if (!aFmtKey.hasValue()) { // we aren't bound to a field (or this field's format is invalid) // -> determine the standard text (or numeric) format of the supplier Reference xTypes(xSupplier->getNumberFormats(), UNO_QUERY); if (xTypes.is()) { Locale aApplicationLocale = Application::GetSettings().GetUILanguageTag().getLocale(); if (m_bOriginalNumeric) aFmtKey <<= xTypes->getStandardFormat(NumberFormat::NUMBER, aApplicationLocale); else aFmtKey <<= xTypes->getStandardFormat(NumberFormat::TEXT, aApplicationLocale); } } aSupplier >>= m_xOriginalFormatter; m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier)); m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, aFmtKey); // Adapt the NumericFalg to my bound field if (xField.is()) { m_bNumeric = false; switch (nType) { case DataType::BIT: case DataType::BOOLEAN: case DataType::TINYINT: case DataType::SMALLINT: case DataType::INTEGER: case DataType::BIGINT: case DataType::FLOAT: case DataType::REAL: case DataType::DOUBLE: case DataType::NUMERIC: case DataType::DECIMAL: case DataType::DATE: case DataType::TIME: case DataType::TIMESTAMP: m_bNumeric = true; break; } } else m_bNumeric = m_bOriginalNumeric; setPropertyValue(PROPERTY_TREATASNUMERIC, Any(m_bNumeric)); OSL_VERIFY( aFmtKey >>= nFormatKey ); } } } Reference xSupplier = calcFormatsSupplier(); m_bNumeric = getBOOL( getPropertyValue( PROPERTY_TREATASNUMERIC ) ); m_nKeyType = getNumberFormatType( xSupplier->getNumberFormats(), nFormatKey ); xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate; OEditBaseModel::onConnectedDbColumn( _rxForm ); } void OFormattedModel::onDisconnectedDbColumn() { OEditBaseModel::onDisconnectedDbColumn(); if (m_xOriginalFormatter.is()) { // Our aggregated model does not hold any Format information m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(m_xOriginalFormatter)); m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, Any()); setPropertyValue(PROPERTY_TREATASNUMERIC, Any(m_bOriginalNumeric)); m_xOriginalFormatter = nullptr; } m_nKeyType = NumberFormat::UNDEFINED; m_aNullDate = DBTypeConversion::getStandardDate(); } void OFormattedModel::write(const Reference& _rxOutStream) { OEditBaseModel::write(_rxOutStream); _rxOutStream->writeShort(0x0003); DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::write : have no aggregate !"); // Bring my Format (may be void) to a persistent Format. // The Supplier together with the Key is already persistent, but that doesn't mean // we have to save the Supplier (which would be quite some overhead) Reference xSupplier; Any aFmtKey; bool bVoidKey = true; if (m_xAggregateSet.is()) { Any aSupplier = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER); if (aSupplier.getValueType().getTypeClass() != TypeClass_VOID) { OSL_VERIFY( aSupplier >>= xSupplier ); } aFmtKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY); bVoidKey = (!xSupplier.is() || !aFmtKey.hasValue()) || (isLoaded() && m_xOriginalFormatter.is()); // (no Format and/or Key) OR (loaded and faked Formatter) } _rxOutStream->writeBoolean(!bVoidKey); if (!bVoidKey) { // Create persistent values from the FormatKey and the Formatter Any aKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY); sal_Int32 nKey = aKey.hasValue() ? getINT32(aKey) : 0; Reference xFormats = xSupplier->getNumberFormats(); OUString sFormatDescription; LanguageType eFormatLanguage = LANGUAGE_DONTKNOW; static constexpr OUString s_aLocaleProp = u"Locale"_ustr; Reference xFormat = xFormats->getByKey(nKey); if (hasProperty(s_aLocaleProp, xFormat)) { Any aLocale = xFormat->getPropertyValue(s_aLocaleProp); DBG_ASSERT(aLocale.has(), "OFormattedModel::write : invalid language property !"); if (auto pLocale = o3tl::tryAccess(aLocale)) { eFormatLanguage = LanguageTag::convertToLanguageType( *pLocale, false); } } static constexpr OUString s_aFormatStringProp = u"FormatString"_ustr; if (hasProperty(s_aFormatStringProp, xFormat)) xFormat->getPropertyValue(s_aFormatStringProp) >>= sFormatDescription; _rxOutStream->writeUTF(sFormatDescription); _rxOutStream->writeLong(static_cast(eFormatLanguage)); } // version 2 : write the properties common to all OEditBaseModels writeCommonEditProperties(_rxOutStream); // version 3 : write the effective value property of the aggregate // Due to a bug within the UnoControlFormattedFieldModel implementation (our default aggregate) // this props value isn't correctly read and this can't be corrected without being incompatible. // so we have our own handling. // and to be a little bit more compatible we make the following section skippable { OStreamSection aDownCompat(_rxOutStream); // a sub version within the skippable block _rxOutStream->writeShort(0x0000); // version 0: the effective value of the aggregate Any aEffectiveValue; if (m_xAggregateSet.is()) { try { aEffectiveValue = m_xAggregateSet->getPropertyValue(PROPERTY_EFFECTIVE_VALUE); } catch(const Exception&) { } } { OStreamSection aDownCompat2(_rxOutStream); switch (aEffectiveValue.getValueType().getTypeClass()) { case TypeClass_STRING: _rxOutStream->writeShort(0x0000); _rxOutStream->writeUTF(::comphelper::getString(aEffectiveValue)); break; case TypeClass_DOUBLE: _rxOutStream->writeShort(0x0001); _rxOutStream->writeDouble(::comphelper::getDouble(aEffectiveValue)); break; default: // void and all unknown states DBG_ASSERT(!aEffectiveValue.hasValue(), "FmXFormattedModel::write : unknown property value type !"); _rxOutStream->writeShort(0x0002); break; } } } } void OFormattedModel::read(const Reference& _rxInStream) { OEditBaseModel::read(_rxInStream); sal_uInt16 nVersion = _rxInStream->readShort(); Reference xSupplier; sal_Int32 nKey = -1; switch (nVersion) { case 0x0001 : case 0x0002 : case 0x0003 : { bool bNonVoidKey = _rxInStream->readBoolean(); if (bNonVoidKey) { // read string and language... OUString sFormatDescription = _rxInStream->readUTF(); LanguageType eDescriptionLanguage(_rxInStream->readLong()); // and let a formatter roll dice based on that to create a key... xSupplier = calcFormatsSupplier(); // calcFormatsSupplier first takes the one from the model, then one from the starform, then a new one... Reference xFormats = xSupplier->getNumberFormats(); if (xFormats.is()) { Locale aDescriptionLanguage( LanguageTag::convertToLocale(eDescriptionLanguage)); nKey = xFormats->queryKey(sFormatDescription, aDescriptionLanguage, false); if (nKey == sal_Int32(-1)) { // does not yet exist in my formatter... nKey = xFormats->addNew(sFormatDescription, aDescriptionLanguage); } } } if ((nVersion == 0x0002) || (nVersion == 0x0003)) readCommonEditProperties(_rxInStream); if (nVersion == 0x0003) { // since version 3 there is a "skippable" block at this position OStreamSection aDownCompat(_rxInStream); _rxInStream->readShort(); // sub-version // version 0 and higher: the "effective value" property Any aEffectiveValue; { OStreamSection aDownCompat2(_rxInStream); switch (_rxInStream->readShort()) { case 0: // String aEffectiveValue <<= _rxInStream->readUTF(); break; case 1: // double aEffectiveValue <<= _rxInStream->readDouble(); break; case 2: break; case 3: OSL_FAIL("FmXFormattedModel::read : unknown effective value type!"); } } // this property is only to be set if we have no control source: in all other cases the base class made a // reset after it's read and this set the effective value to a default value if ( m_xAggregateSet.is() && getControlSource().isEmpty() ) { try { m_xAggregateSet->setPropertyValue(PROPERTY_EFFECTIVE_VALUE, aEffectiveValue); } catch(const Exception&) { } } } } break; default : OSL_FAIL("OFormattedModel::read : unknown version !"); // then the format of the aggregated set stay like it was during creation: void defaultCommonEditProperties(); break; } if ((nKey != -1) && m_xAggregateSet.is()) { m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier)); m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, Any(nKey)); } else { setPropertyToDefault(PROPERTY_FORMATSSUPPLIER); setPropertyToDefault(PROPERTY_FORMATKEY); } } sal_uInt16 OFormattedModel::getPersistenceFlags() const { return (OEditBaseModel::getPersistenceFlags() & ~PF_HANDLE_COMMON_PROPS); // a) we do our own call to writeCommonEditProperties } bool OFormattedModel::commitControlValueToDbColumn( bool /*_bPostReset*/ ) { Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) ); if ( aControlValue == m_aSaveValue ) return true; // empty string + EmptyIsNull = void if ( !aControlValue.hasValue() || ( ( aControlValue.getValueType().getTypeClass() == TypeClass_STRING ) && getString( aControlValue ).isEmpty() && m_bEmptyIsNull ) ) m_xColumnUpdate->updateNull(); else { try { double f = 0.0; if ( aControlValue.getValueType().getTypeClass() == TypeClass_DOUBLE || (aControlValue >>= f)) // #i110323 { DBTypeConversion::setValue( m_xColumnUpdate, m_aNullDate, getDouble( aControlValue ), m_nKeyType ); } else { DBG_ASSERT( aControlValue.getValueType().getTypeClass() == TypeClass_STRING, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" ); m_xColumnUpdate->updateString( getString( aControlValue ) ); } } catch(const Exception&) { return false; } } m_aSaveValue = aControlValue; return true; } void OFormattedModel::onConnectedExternalValue( ) { OEditBaseModel::onConnectedExternalValue(); updateFormatterNullDate(); } Any OFormattedModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const { Any aControlValue; switch( _rExternalValue.getValueTypeClass() ) { case TypeClass_VOID: break; case TypeClass_STRING: aControlValue = _rExternalValue; break; case TypeClass_BOOLEAN: { bool bExternalValue = false; _rExternalValue >>= bExternalValue; aControlValue <<= static_cast( bExternalValue ? 1 : 0 ); } break; default: { if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::Date >::get() ) ) { css::util::Date aDate; _rExternalValue >>= aDate; aControlValue <<= DBTypeConversion::toDouble( aDate, m_aNullDate ); } else if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::Time >::get() ) ) { css::util::Time aTime; _rExternalValue >>= aTime; aControlValue <<= DBTypeConversion::toDouble( aTime ); } else if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::DateTime >::get() ) ) { css::util::DateTime aDateTime; _rExternalValue >>= aDateTime; aControlValue <<= DBTypeConversion::toDouble( aDateTime, m_aNullDate ); } else { OSL_ENSURE( _rExternalValue.getValueTypeClass() == TypeClass_DOUBLE, "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" ); double fValue = 0; OSL_VERIFY( _rExternalValue >>= fValue ); aControlValue <<= fValue; } } } return aControlValue; } Any OFormattedModel::translateControlValueToExternalValue( ) const { OSL_PRECOND( hasExternalValueBinding(), "OFormattedModel::translateControlValueToExternalValue: precondition not met!" ); Any aControlValue( getControlValue() ); if ( !aControlValue.hasValue() ) return aControlValue; Any aExternalValue; // translate into the external value type Type aExternalValueType( getExternalValueType() ); switch ( aExternalValueType.getTypeClass() ) { case TypeClass_STRING: { OUString sString; if ( aControlValue >>= sString ) { aExternalValue <<= sString; break; } [[fallthrough]]; } case TypeClass_BOOLEAN: { double fValue = 0; OSL_VERIFY( aControlValue >>= fValue ); // if this asserts ... well, the somebody set the TreatAsNumeric property to false, // and the control value is a string. This implies some weird misconfiguration // of the FormattedModel, so we won't care for it for the moment. aExternalValue <<= fValue != 0.0; } break; default: { double fValue = 0; OSL_VERIFY( aControlValue >>= fValue ); // if this asserts ... well, the somebody set the TreatAsNumeric property to false, // and the control value is a string. This implies some weird misconfiguration // of the FormattedModel, so we won't care for it for the moment. if ( aExternalValueType.equals( cppu::UnoType< css::util::Date >::get() ) ) { aExternalValue <<= DBTypeConversion::toDate( fValue, m_aNullDate ); } else if ( aExternalValueType.equals( cppu::UnoType< css::util::Time >::get() ) ) { aExternalValue <<= DBTypeConversion::toTime( fValue ); } else if ( aExternalValueType.equals( cppu::UnoType< css::util::DateTime >::get() ) ) { aExternalValue <<= DBTypeConversion::toDateTime( fValue, m_aNullDate ); } else { OSL_ENSURE( aExternalValueType.equals( cppu::UnoType< double >::get() ), "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" ); aExternalValue <<= fValue; } } break; } return aExternalValue; } Any OFormattedModel::translateDbColumnToControlValue() { if ( m_bNumeric ) m_aSaveValue <<= DBTypeConversion::getValue( m_xColumn, m_aNullDate ); // #100056# OJ else m_aSaveValue <<= m_xColumn->getString(); if ( m_xColumn->wasNull() ) m_aSaveValue.clear(); return m_aSaveValue; } Sequence< Type > OFormattedModel::getSupportedBindingTypes() { ::std::vector< Type > aTypes; switch ( m_nKeyType & ~NumberFormat::DEFINED ) { case NumberFormat::DATE: aTypes.push_back(cppu::UnoType< css::util::Date >::get() ); break; case NumberFormat::TIME: aTypes.push_back(cppu::UnoType< css::util::Time >::get() ); break; case NumberFormat::DATETIME: aTypes.push_back(cppu::UnoType< css::util::DateTime >::get() ); break; case NumberFormat::TEXT: aTypes.push_back(cppu::UnoType< OUString >::get() ); break; case NumberFormat::LOGICAL: aTypes.push_back(cppu::UnoType< sal_Bool >::get() ); break; } aTypes.push_back( cppu::UnoType< double >::get() ); return comphelper::containerToSequence(aTypes); } Any OFormattedModel::getDefaultForReset() const { return m_xAggregateSet->getPropertyValue( PROPERTY_EFFECTIVE_DEFAULT ); } void OFormattedModel::resetNoBroadcast() { OEditBaseModel::resetNoBroadcast(); m_aSaveValue.clear(); } } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* com_sun_star_form_OFormattedControl_get_implementation(css::uno::XComponentContext* component, css::uno::Sequence const &) { return cppu::acquire(new frm::OFormattedControl(component)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */