1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include "FormattedField.hxx"
20 #include <services.hxx>
21 #include <property.hxx>
22 #include <propertybaghelper.hxx>
23 #include <comphelper/property.hxx>
24 #include <comphelper/sequence.hxx>
25 #include <comphelper/numbers.hxx>
26 #include <comphelper/types.hxx>
27 #include <connectivity/dbtools.hxx>
28 #include <connectivity/dbconversion.hxx>
29 #include <o3tl/any.hxx>
30 #include <svl/numformat.hxx>
31 #include <svl/numuno.hxx>
32 #include <vcl/keycodes.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/settings.hxx>
35 #include <tools/debug.hxx>
36 #include <i18nlangtag/languagetag.hxx>
37 #include <com/sun/star/beans/PropertyAttribute.hpp>
38 #include <com/sun/star/sdbc/DataType.hpp>
39 #include <com/sun/star/util/NumberFormat.hpp>
40 #include <com/sun/star/util/Date.hpp>
41 #include <com/sun/star/util/Time.hpp>
42 #include <com/sun/star/awt/MouseEvent.hpp>
43 #include <com/sun/star/form/XSubmit.hpp>
44 #include <com/sun/star/awt/XWindow.hpp>
45 #include <com/sun/star/form/FormComponentType.hpp>
46 #include <com/sun/star/util/XNumberFormatTypes.hpp>
47 #include <com/sun/star/form/XForm.hpp>
48 #include <com/sun/star/container/XIndexAccess.hpp>
49 #include <osl/mutex.hxx>
50 // needed as long as we use the SolarMutex
51 #include <comphelper/streamsection.hxx>
52 #include <cppuhelper/weakref.hxx>
53 #include <unotools/desktopterminationobserver.hxx>
58 using namespace dbtools
;
59 using namespace css::uno
;
60 using namespace css::sdb
;
61 using namespace css::sdbc
;
62 using namespace css::beans
;
63 using namespace css::container
;
64 using namespace css::form
;
65 using namespace css::awt
;
66 using namespace css::io
;
67 using namespace css::lang
;
68 using namespace css::util
;
74 class StandardFormatsSupplier
: public SvNumberFormatsSupplierObj
, public ::utl::ITerminationListener
77 std::unique_ptr
<SvNumberFormatter
> m_pMyPrivateFormatter
;
78 static WeakReference
< XNumberFormatsSupplier
> s_xDefaultFormatsSupplier
;
80 static Reference
< XNumberFormatsSupplier
> get( const Reference
< XComponentContext
>& _rxORB
);
82 StandardFormatsSupplier(const Reference
< XComponentContext
>& _rxFactory
,LanguageType _eSysLanguage
);
83 virtual ~StandardFormatsSupplier() override
;
85 virtual bool queryTermination() const override
;
86 virtual void notifyTermination() override
;
91 WeakReference
< XNumberFormatsSupplier
> StandardFormatsSupplier::s_xDefaultFormatsSupplier
;
92 StandardFormatsSupplier::StandardFormatsSupplier(const Reference
< XComponentContext
> & _rxContext
,LanguageType _eSysLanguage
)
93 :m_pMyPrivateFormatter(new SvNumberFormatter(_rxContext
, _eSysLanguage
))
95 SetNumberFormatter(m_pMyPrivateFormatter
.get());
97 ::utl::DesktopTerminationObserver::registerTerminationListener( this );
99 StandardFormatsSupplier::~StandardFormatsSupplier()
101 ::utl::DesktopTerminationObserver::revokeTerminationListener( this );
103 Reference
< XNumberFormatsSupplier
> StandardFormatsSupplier::get( const Reference
< XComponentContext
>& _rxORB
)
105 LanguageType eSysLanguage
= LANGUAGE_SYSTEM
;
107 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
108 Reference
< XNumberFormatsSupplier
> xSupplier
= s_xDefaultFormatsSupplier
;
109 if ( xSupplier
.is() )
111 // get the Office's locale
112 eSysLanguage
= SvtSysLocale().GetLanguageTag().getLanguageType( false);
114 rtl::Reference
<StandardFormatsSupplier
> pSupplier
= new StandardFormatsSupplier( _rxORB
, eSysLanguage
);
116 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
117 Reference
< XNumberFormatsSupplier
> xSupplier
= s_xDefaultFormatsSupplier
;
118 if ( xSupplier
.is() )
119 // somebody used the small time frame where the mutex was not locked to create and set
122 s_xDefaultFormatsSupplier
= css::uno::Reference
<css::uno::XWeak
>(pSupplier
);
126 bool StandardFormatsSupplier::queryTermination() const
130 void StandardFormatsSupplier::notifyTermination()
132 Reference
< XNumberFormatsSupplier
> xKeepAlive
= this;
133 // when the application is terminating, release our static reference so that we are cleared/destructed
134 // earlier than upon unloading the library
136 s_xDefaultFormatsSupplier
= WeakReference
< XNumberFormatsSupplier
>( );
137 SetNumberFormatter( nullptr );
138 m_pMyPrivateFormatter
.reset();
140 Sequence
<Type
> OFormattedControl::_getTypes()
142 return ::comphelper::concatSequences(
143 OFormattedControl_BASE::getTypes(),
144 OBoundControl::_getTypes()
147 Any SAL_CALL
OFormattedControl::queryAggregation(const Type
& _rType
)
149 Any aReturn
= OBoundControl::queryAggregation(_rType
);
150 if (!aReturn
.hasValue())
151 aReturn
= OFormattedControl_BASE::queryInterface(_rType
);
154 OFormattedControl::OFormattedControl(const Reference
<XComponentContext
>& _rxFactory
)
155 :OBoundControl(_rxFactory
, VCL_CONTROL_FORMATTEDFIELD
)
156 ,m_nKeyEvent(nullptr)
158 osl_atomic_increment(&m_refCount
);
160 if (auto xComp
= query_aggregation
<XWindow
>(m_xAggregate
))
161 xComp
->addKeyListener(this);
163 osl_atomic_decrement(&m_refCount
);
165 OFormattedControl::~OFormattedControl()
168 Application::RemoveUserEvent( m_nKeyEvent
);
169 if (!OComponentHelper::rBHelper
.bDisposed
)
177 void OFormattedControl::disposing(const EventObject
& _rSource
)
179 OBoundControl::disposing(_rSource
);
181 void OFormattedControl::keyPressed(const css::awt::KeyEvent
& e
)
183 if( e
.KeyCode
!= KEY_RETURN
|| e
.Modifiers
!= 0 )
185 // Is the control located in a form with a Submit URL?
186 Reference
<css::beans::XPropertySet
> xSet(getModel(), UNO_QUERY
);
189 Reference
<XFormComponent
> xFComp(xSet
, UNO_QUERY
);
190 css::uno::Reference
<css::uno::XInterface
> xParent
= xFComp
->getParent();
193 Reference
<css::beans::XPropertySet
> xFormSet(xParent
, UNO_QUERY
);
196 Any
aTmp(xFormSet
->getPropertyValue( PROPERTY_TARGET_URL
));
197 if (!aTmp
.has
<OUString
>() ||
198 getString(aTmp
).isEmpty() )
200 Reference
<XIndexAccess
> xElements(xParent
, UNO_QUERY
);
201 sal_Int32 nCount
= xElements
->getCount();
204 Reference
<css::beans::XPropertySet
> xFCSet
;
205 for( sal_Int32 nIndex
=0; nIndex
< nCount
; nIndex
++ )
207 // Any aElement(xElements->getByIndex(nIndex));
208 xElements
->getByIndex(nIndex
) >>= xFCSet
;
209 if (hasProperty(PROPERTY_CLASSID
, xFCSet
) &&
210 getINT16(xFCSet
->getPropertyValue(PROPERTY_CLASSID
)) == FormComponentType::TEXTFIELD
)
212 // Found another Edit -> Do not submit then
218 // Because we're still in the Handler, execute submit asynchronously
220 Application::RemoveUserEvent( m_nKeyEvent
);
221 m_nKeyEvent
= Application::PostUserEvent( LINK(this, OFormattedControl
,
225 void OFormattedControl::keyReleased(const css::awt::KeyEvent
& /*e*/)
229 IMPL_LINK_NOARG(OFormattedControl
, OnKeyPressed
, void*, void)
231 m_nKeyEvent
= nullptr;
232 Reference
<XFormComponent
> xFComp(getModel(), UNO_QUERY
);
233 css::uno::Reference
<css::uno::XInterface
> xParent
= xFComp
->getParent();
234 Reference
<XSubmit
> xSubmit(xParent
, UNO_QUERY
);
236 xSubmit
->submit( Reference
<XControl
> (), css::awt::MouseEvent() );
239 css::uno::Sequence
<OUString
> OFormattedControl::getSupportedServiceNames()
241 css::uno::Sequence
<OUString
> aSupported
= OBoundControl::getSupportedServiceNames();
242 aSupported
.realloc(aSupported
.getLength() + 2);
243 OUString
*pArray
= aSupported
.getArray();
244 pArray
[aSupported
.getLength()-2] = FRM_SUN_CONTROL_FORMATTEDFIELD
;
245 pArray
[aSupported
.getLength()-1] = STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD
;
249 void OFormattedModel::implConstruct()
252 m_bOriginalNumeric
= false;
254 m_xOriginalFormatter
= nullptr;
255 m_nKeyType
= NumberFormat::UNDEFINED
;
256 m_aNullDate
= DBTypeConversion::getStandardDate();
257 // default our formats supplier
258 osl_atomic_increment(&m_refCount
);
259 setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
260 osl_atomic_decrement(&m_refCount
);
261 startAggregatePropertyListening( PROPERTY_FORMATKEY
);
262 startAggregatePropertyListening( PROPERTY_FORMATSSUPPLIER
);
264 OFormattedModel::OFormattedModel(const Reference
<XComponentContext
>& _rxFactory
)
265 :OEditBaseModel(_rxFactory
, VCL_CONTROLMODEL_FORMATTEDFIELD
, FRM_SUN_CONTROL_FORMATTEDFIELD
, true, true )
266 // use the old control name for compatibility reasons
267 ,OErrorBroadcaster( OComponentHelper::rBHelper
)
270 m_nClassId
= FormComponentType::TEXTFIELD
;
271 initValueProperty( PROPERTY_EFFECTIVE_VALUE
, PROPERTY_ID_EFFECTIVE_VALUE
);
273 OFormattedModel::OFormattedModel( const OFormattedModel
* _pOriginal
, const Reference
< XComponentContext
>& _rxFactory
)
274 :OEditBaseModel( _pOriginal
, _rxFactory
)
275 ,OErrorBroadcaster( OComponentHelper::rBHelper
)
280 OFormattedModel::~OFormattedModel()
285 css::uno::Reference
< css::util::XCloneable
> SAL_CALL
OFormattedModel::createClone()
287 rtl::Reference
<OFormattedModel
> pClone
= new OFormattedModel(this, getContext());
288 pClone
->clonedFrom(this);
292 void SAL_CALL
OFormattedModel::disposing()
294 OErrorBroadcaster::disposing();
295 OEditBaseModel::disposing();
299 css::uno::Sequence
<OUString
> OFormattedModel::getSupportedServiceNames()
301 css::uno::Sequence
<OUString
> aSupported
= OEditBaseModel::getSupportedServiceNames();
302 sal_Int32 nOldLen
= aSupported
.getLength();
303 aSupported
.realloc( nOldLen
+ 9 );
304 OUString
* pStoreTo
= aSupported
.getArray() + nOldLen
;
305 *pStoreTo
++ = BINDABLE_CONTROL_MODEL
;
306 *pStoreTo
++ = DATA_AWARE_CONTROL_MODEL
;
307 *pStoreTo
++ = VALIDATABLE_CONTROL_MODEL
;
308 *pStoreTo
++ = BINDABLE_DATA_AWARE_CONTROL_MODEL
;
309 *pStoreTo
++ = VALIDATABLE_BINDABLE_CONTROL_MODEL
;
310 *pStoreTo
++ = FRM_SUN_COMPONENT_FORMATTEDFIELD
;
311 *pStoreTo
++ = FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD
;
312 *pStoreTo
++ = BINDABLE_DATABASE_FORMATTED_FIELD
;
313 *pStoreTo
++ = FRM_COMPONENT_FORMATTEDFIELD
;
318 Any SAL_CALL
OFormattedModel::queryAggregation(const Type
& _rType
)
320 Any aReturn
= OEditBaseModel::queryAggregation( _rType
);
321 return aReturn
.hasValue() ? aReturn
: OErrorBroadcaster::queryInterface( _rType
);
325 Sequence
< Type
> OFormattedModel::_getTypes()
327 return ::comphelper::concatSequences(
328 OEditBaseModel::_getTypes(),
329 OErrorBroadcaster::getTypes()
334 OUString SAL_CALL
OFormattedModel::getServiceName()
336 return FRM_COMPONENT_EDIT
;
340 void OFormattedModel::describeFixedProperties( Sequence
< Property
>& _rProps
) const
342 OEditBaseModel::describeFixedProperties( _rProps
);
343 sal_Int32 nOldCount
= _rProps
.getLength();
344 _rProps
.realloc( nOldCount
+ 3);
345 css::beans::Property
* pProperties
= _rProps
.getArray() + nOldCount
;
346 *pProperties
++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL
, PROPERTY_ID_EMPTY_IS_NULL
, cppu::UnoType
<bool>::get(),
347 css::beans::PropertyAttribute::BOUND
);
348 *pProperties
++ = css::beans::Property(PROPERTY_TABINDEX
, PROPERTY_ID_TABINDEX
, cppu::UnoType
<sal_Int16
>::get(), css::beans::PropertyAttribute::BOUND
);
349 *pProperties
++ = css::beans::Property(PROPERTY_FILTERPROPOSAL
, PROPERTY_ID_FILTERPROPOSAL
, cppu::UnoType
<bool>::get(),
350 css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::MAYBEDEFAULT
);
351 DBG_ASSERT( pProperties
== _rProps
.getArray() + _rProps
.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
354 void OFormattedModel::describeAggregateProperties( Sequence
< Property
>& _rAggregateProps
) const
356 OEditBaseModel::describeAggregateProperties( _rAggregateProps
);
357 // TreatAsNumeric is not transient: we want to attach it to the UI
358 // This is necessary to make EffectiveDefault (which may be text or a number) meaningful
359 ModifyPropertyAttributes(_rAggregateProps
, PROPERTY_TREATASNUMERIC
, 0, PropertyAttribute::TRANSIENT
);
360 // Same for FormatKey
361 // (though the paragraph above for the TreatAsNumeric does not hold anymore - we do not have an UI for this.
362 // But we have for the format key ...)
363 ModifyPropertyAttributes(_rAggregateProps
, PROPERTY_FORMATKEY
, 0, PropertyAttribute::TRANSIENT
);
364 RemoveProperty(_rAggregateProps
, PROPERTY_STRICTFORMAT
);
365 // no strict format property for formatted fields: it does not make sense, 'cause
366 // there is no general way to decide which characters/sub strings are allowed during the input of an
367 // arbitrary formatted control
370 void OFormattedModel::setPropertyToDefaultByHandle(sal_Int32 nHandle
)
372 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
374 Reference
<XNumberFormatsSupplier
> xSupplier
= calcDefaultFormatsSupplier();
375 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::setPropertyToDefaultByHandle(FORMATSSUPPLIER) : have no aggregate !");
376 if (m_xAggregateSet
.is())
377 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, Any(xSupplier
));
380 OEditBaseModel::setPropertyToDefaultByHandle(nHandle
);
383 void OFormattedModel::setPropertyToDefault(const OUString
& aPropertyName
)
385 OPropertyArrayAggregationHelper
& rPH
= m_aPropertyBagHelper
.getInfoHelper();
386 sal_Int32 nHandle
= rPH
.getHandleByName( aPropertyName
);
387 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
388 setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
390 OEditBaseModel::setPropertyToDefault(aPropertyName
);
393 Any
OFormattedModel::getPropertyDefaultByHandle( sal_Int32 nHandle
) const
395 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
397 Reference
<XNumberFormatsSupplier
> xSupplier
= calcDefaultFormatsSupplier();
398 return Any(xSupplier
);
401 return OEditBaseModel::getPropertyDefaultByHandle(nHandle
);
404 Any SAL_CALL
OFormattedModel::getPropertyDefault( const OUString
& aPropertyName
)
406 OPropertyArrayAggregationHelper
& rPH
= m_aPropertyBagHelper
.getInfoHelper();
407 sal_Int32 nHandle
= rPH
.getHandleByName( aPropertyName
);
408 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
409 return getPropertyDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
411 return OEditBaseModel::getPropertyDefault(aPropertyName
);
414 void OFormattedModel::_propertyChanged( const css::beans::PropertyChangeEvent
& evt
)
416 // TODO: check how this works with external bindings
417 OSL_ENSURE( evt
.Source
== m_xAggregateSet
, "OFormattedModel::_propertyChanged: where did this come from?" );
418 if ( evt
.Source
!= m_xAggregateSet
)
421 if ( evt
.PropertyName
== PROPERTY_FORMATKEY
)
423 if ( evt
.NewValue
.getValueTypeClass() == TypeClass_LONG
)
427 ::osl::MutexGuard
aGuard( m_aMutex
);
428 Reference
<XNumberFormatsSupplier
> xSupplier( calcFormatsSupplier() );
429 m_nKeyType
= getNumberFormatType(xSupplier
->getNumberFormats(), getINT32( evt
.NewValue
) );
430 // as m_aSaveValue (which is used by commitControlValueToDbColumn) is format dependent we have
431 // to recalc it, which is done by translateDbColumnToControlValue
432 if ( m_xColumn
.is() && m_xAggregateFastSet
.is() && !m_xCursor
->isBeforeFirst() && !m_xCursor
->isAfterLast())
434 setControlValue( translateDbColumnToControlValue(), eOther
);
436 // if we're connected to an external value binding, then re-calculate the type
437 // used to exchange the value - it depends on the format, too
438 if ( hasExternalValueBinding() )
440 calculateExternalValueType();
443 catch(const Exception
&)
449 if ( evt
.PropertyName
== PROPERTY_FORMATSSUPPLIER
)
451 updateFormatterNullDate();
454 OBoundControlModel::_propertyChanged( evt
);
457 void OFormattedModel::updateFormatterNullDate()
459 // calc the current NULL date
460 Reference
< XNumberFormatsSupplier
> xSupplier( calcFormatsSupplier() );
461 if ( xSupplier
.is() )
462 xSupplier
->getNumberFormatSettings()->getPropertyValue(u
"NullDate"_ustr
) >>= m_aNullDate
;
465 Reference
< XNumberFormatsSupplier
> OFormattedModel::calcFormatsSupplier() const
467 Reference
<XNumberFormatsSupplier
> xSupplier
;
468 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::calcFormatsSupplier : have no aggregate !");
469 // Does my aggregate model have a FormatSupplier?
470 if( m_xAggregateSet
.is() )
471 m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
) >>= xSupplier
;
473 // check if my parent form has a supplier
474 xSupplier
= calcFormFormatsSupplier();
476 xSupplier
= calcDefaultFormatsSupplier();
477 DBG_ASSERT(xSupplier
.is(), "OFormattedModel::calcFormatsSupplier : no supplier !");
478 // We should have one by now
482 Reference
<XNumberFormatsSupplier
> OFormattedModel::calcFormFormatsSupplier() const
484 Reference
<XChild
> xMe(const_cast<OFormattedModel
*>(this));
485 // By this we make sure that we get the right object even when aggregating
486 DBG_ASSERT(xMe
.is(), "OFormattedModel::calcFormFormatsSupplier : I should have a content interface !");
487 // Iterate through until we reach a StartForm (starting with an own Parent)
488 Reference
<XChild
> xParent(xMe
->getParent(), UNO_QUERY
);
489 Reference
<XForm
> xNextParentForm(xParent
, UNO_QUERY
);
490 while (!xNextParentForm
.is() && xParent
.is())
492 xParent
.set(xParent
->getParent(), css::uno::UNO_QUERY
);
493 xNextParentForm
.set(xParent
, css::uno::UNO_QUERY
);
495 if (!xNextParentForm
.is())
497 OSL_FAIL("OFormattedModel::calcFormFormatsSupplier : have no ancestor which is a form !");
500 // The FormatSupplier of my ancestor (if it has one)
501 Reference
< XRowSet
> xRowSet( xNextParentForm
, UNO_QUERY
);
502 Reference
< XNumberFormatsSupplier
> xSupplier
;
504 xSupplier
= getNumberFormats( getConnection(xRowSet
), true, getContext() );
508 Reference
< XNumberFormatsSupplier
> OFormattedModel::calcDefaultFormatsSupplier() const
510 return StandardFormatsSupplier::get( getContext() );
514 void OFormattedModel::loaded(const EventObject
& rEvent
)
516 // HACK: our onConnectedDbColumn accesses our NumberFormatter which locks the solar mutex (as it doesn't have
517 // an own one). To prevent deadlocks with other threads which may request a property from us in an
518 // UI-triggered action (e.g. a tooltip) we lock the solar mutex _here_ before our base class locks
519 // its own mutex (which is used for property requests)
520 // alternative a): we use two mutexes, one which is passed to the OPropertysetHelper and used for
521 // property requests and one for our own code. This would need a lot of code rewriting
522 // alternative b): The NumberFormatter has to be really threadsafe (with an own mutex), which is
523 // the only "clean" solution for me.
524 SolarMutexGuard aGuard
;
525 OEditBaseModel::loaded(rEvent
);
528 void OFormattedModel::onConnectedDbColumn( const Reference
< XInterface
>& _rxForm
)
530 m_xOriginalFormatter
= nullptr;
531 // get some properties of the field
532 Reference
<XPropertySet
> xField
= getField();
533 sal_Int32 nFormatKey
= 0;
534 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::onConnectedDbColumn : have no aggregate !");
535 if (m_xAggregateSet
.is())
536 { // all the following doesn't make any sense if we have no aggregate ...
537 Any aSupplier
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
);
538 DBG_ASSERT( aSupplier
.hasValue(), "OFormattedModel::onConnectedDbColumn : invalid property value !" );
539 // This should've been set to the correct value in the ctor or in the read
540 Any aFmtKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
541 if ( !(aFmtKey
>>= nFormatKey
) )
542 { // nobody gave us a format to use. So we examine the field we're bound to for a
543 // format key, and use it ourself, too
544 sal_Int32 nType
= DataType::VARCHAR
;
547 aFmtKey
= xField
->getPropertyValue(PROPERTY_FORMATKEY
);
548 xField
->getPropertyValue(PROPERTY_FIELDTYPE
) >>= nType
;
550 Reference
<XNumberFormatsSupplier
> xSupplier
= calcFormFormatsSupplier();
551 DBG_ASSERT(xSupplier
.is(), "OFormattedModel::onConnectedDbColumn : bound to a field but no parent with a formatter ? how this ?");
554 m_bOriginalNumeric
= getBOOL(getPropertyValue(PROPERTY_TREATASNUMERIC
));
555 if (!aFmtKey
.hasValue())
556 { // we aren't bound to a field (or this field's format is invalid)
557 // -> determine the standard text (or numeric) format of the supplier
558 Reference
<XNumberFormatTypes
> xTypes(xSupplier
->getNumberFormats(), UNO_QUERY
);
561 Locale aApplicationLocale
= Application::GetSettings().GetUILanguageTag().getLocale();
562 if (m_bOriginalNumeric
)
563 aFmtKey
<<= xTypes
->getStandardFormat(NumberFormat::NUMBER
, aApplicationLocale
);
565 aFmtKey
<<= xTypes
->getStandardFormat(NumberFormat::TEXT
, aApplicationLocale
);
568 aSupplier
>>= m_xOriginalFormatter
;
569 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, Any(xSupplier
));
570 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, aFmtKey
);
571 // Adapt the NumericFalg to my bound field
578 case DataType::BOOLEAN
:
579 case DataType::TINYINT
:
580 case DataType::SMALLINT
:
581 case DataType::INTEGER
:
582 case DataType::BIGINT
:
583 case DataType::FLOAT
:
585 case DataType::DOUBLE
:
586 case DataType::NUMERIC
:
587 case DataType::DECIMAL
:
590 case DataType::TIMESTAMP
:
596 m_bNumeric
= m_bOriginalNumeric
;
597 setPropertyValue(PROPERTY_TREATASNUMERIC
, Any(m_bNumeric
));
598 OSL_VERIFY( aFmtKey
>>= nFormatKey
);
602 Reference
<XNumberFormatsSupplier
> xSupplier
= calcFormatsSupplier();
603 m_bNumeric
= getBOOL( getPropertyValue( PROPERTY_TREATASNUMERIC
) );
604 m_nKeyType
= getNumberFormatType( xSupplier
->getNumberFormats(), nFormatKey
);
605 xSupplier
->getNumberFormatSettings()->getPropertyValue(u
"NullDate"_ustr
) >>= m_aNullDate
;
606 OEditBaseModel::onConnectedDbColumn( _rxForm
);
609 void OFormattedModel::onDisconnectedDbColumn()
611 OEditBaseModel::onDisconnectedDbColumn();
612 if (m_xOriginalFormatter
.is())
613 { // Our aggregated model does not hold any Format information
614 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, Any(m_xOriginalFormatter
));
615 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, Any());
616 setPropertyValue(PROPERTY_TREATASNUMERIC
, Any(m_bOriginalNumeric
));
617 m_xOriginalFormatter
= nullptr;
619 m_nKeyType
= NumberFormat::UNDEFINED
;
620 m_aNullDate
= DBTypeConversion::getStandardDate();
623 void OFormattedModel::write(const Reference
<XObjectOutputStream
>& _rxOutStream
)
625 OEditBaseModel::write(_rxOutStream
);
626 _rxOutStream
->writeShort(0x0003);
627 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::write : have no aggregate !");
628 // Bring my Format (may be void) to a persistent Format.
629 // The Supplier together with the Key is already persistent, but that doesn't mean
630 // we have to save the Supplier (which would be quite some overhead)
631 Reference
<XNumberFormatsSupplier
> xSupplier
;
633 bool bVoidKey
= true;
634 if (m_xAggregateSet
.is())
636 Any aSupplier
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
);
637 if (aSupplier
.getValueTypeClass() != TypeClass_VOID
)
639 OSL_VERIFY( aSupplier
>>= xSupplier
);
641 aFmtKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
642 bVoidKey
= (!xSupplier
.is() || !aFmtKey
.hasValue()) || (isLoaded() && m_xOriginalFormatter
.is());
643 // (no Format and/or Key) OR (loaded and faked Formatter)
645 _rxOutStream
->writeBoolean(!bVoidKey
);
648 // Create persistent values from the FormatKey and the Formatter
649 Any aKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
650 sal_Int32 nKey
= aKey
.hasValue() ? getINT32(aKey
) : 0;
651 Reference
<XNumberFormats
> xFormats
= xSupplier
->getNumberFormats();
652 OUString sFormatDescription
;
653 LanguageType eFormatLanguage
= LANGUAGE_DONTKNOW
;
654 static constexpr OUString s_aLocaleProp
= u
"Locale"_ustr
;
655 Reference
<css::beans::XPropertySet
> xFormat
= xFormats
->getByKey(nKey
);
656 if (hasProperty(s_aLocaleProp
, xFormat
))
658 Any aLocale
= xFormat
->getPropertyValue(s_aLocaleProp
);
659 DBG_ASSERT(aLocale
.has
<Locale
>(), "OFormattedModel::write : invalid language property !");
660 if (auto pLocale
= o3tl::tryAccess
<Locale
>(aLocale
))
662 eFormatLanguage
= LanguageTag::convertToLanguageType( *pLocale
, false);
665 static constexpr OUString s_aFormatStringProp
= u
"FormatString"_ustr
;
666 if (hasProperty(s_aFormatStringProp
, xFormat
))
667 xFormat
->getPropertyValue(s_aFormatStringProp
) >>= sFormatDescription
;
668 _rxOutStream
->writeUTF(sFormatDescription
);
669 _rxOutStream
->writeLong(static_cast<sal_uInt16
>(eFormatLanguage
));
671 // version 2 : write the properties common to all OEditBaseModels
672 writeCommonEditProperties(_rxOutStream
);
673 // version 3 : write the effective value property of the aggregate
674 // Due to a bug within the UnoControlFormattedFieldModel implementation (our default aggregate)
675 // this props value isn't correctly read and this can't be corrected without being incompatible.
676 // so we have our own handling.
677 // and to be a little bit more compatible we make the following section skippable
679 OStreamSection
aDownCompat(_rxOutStream
);
680 // a sub version within the skippable block
681 _rxOutStream
->writeShort(0x0000);
682 // version 0: the effective value of the aggregate
684 if (m_xAggregateSet
.is())
686 try { aEffectiveValue
= m_xAggregateSet
->getPropertyValue(PROPERTY_EFFECTIVE_VALUE
); } catch(const Exception
&) { }
689 OStreamSection
aDownCompat2(_rxOutStream
);
690 switch (aEffectiveValue
.getValueTypeClass())
692 case TypeClass_STRING
:
693 _rxOutStream
->writeShort(0x0000);
694 _rxOutStream
->writeUTF(::comphelper::getString(aEffectiveValue
));
696 case TypeClass_DOUBLE
:
697 _rxOutStream
->writeShort(0x0001);
698 _rxOutStream
->writeDouble(::comphelper::getDouble(aEffectiveValue
));
700 default: // void and all unknown states
701 DBG_ASSERT(!aEffectiveValue
.hasValue(), "FmXFormattedModel::write : unknown property value type !");
702 _rxOutStream
->writeShort(0x0002);
709 void OFormattedModel::read(const Reference
<XObjectInputStream
>& _rxInStream
)
711 OEditBaseModel::read(_rxInStream
);
712 sal_uInt16 nVersion
= _rxInStream
->readShort();
713 Reference
<XNumberFormatsSupplier
> xSupplier
;
721 bool bNonVoidKey
= _rxInStream
->readBoolean();
724 // read string and language...
725 OUString sFormatDescription
= _rxInStream
->readUTF();
726 LanguageType
eDescriptionLanguage(_rxInStream
->readLong());
727 // and let a formatter roll dice based on that to create a key...
728 xSupplier
= calcFormatsSupplier();
729 // calcFormatsSupplier first takes the one from the model, then one from the starform, then a new one...
730 Reference
<XNumberFormats
> xFormats
= xSupplier
->getNumberFormats();
733 Locale
aDescriptionLanguage( LanguageTag::convertToLocale(eDescriptionLanguage
));
734 nKey
= xFormats
->queryKey(sFormatDescription
, aDescriptionLanguage
, false);
735 if (nKey
== sal_Int32(-1))
736 { // does not yet exist in my formatter...
737 nKey
= xFormats
->addNew(sFormatDescription
, aDescriptionLanguage
);
741 if ((nVersion
== 0x0002) || (nVersion
== 0x0003))
742 readCommonEditProperties(_rxInStream
);
743 if (nVersion
== 0x0003)
744 { // since version 3 there is a "skippable" block at this position
745 OStreamSection
aDownCompat(_rxInStream
);
746 _rxInStream
->readShort(); // sub-version
747 // version 0 and higher: the "effective value" property
750 OStreamSection
aDownCompat2(_rxInStream
);
751 switch (_rxInStream
->readShort())
754 aEffectiveValue
<<= _rxInStream
->readUTF();
757 aEffectiveValue
<<= _rxInStream
->readDouble();
762 OSL_FAIL("FmXFormattedModel::read : unknown effective value type!");
765 // this property is only to be set if we have no control source: in all other cases the base class made a
766 // reset after it's read and this set the effective value to a default value
767 if ( m_xAggregateSet
.is() && getControlSource().isEmpty() )
771 m_xAggregateSet
->setPropertyValue(PROPERTY_EFFECTIVE_VALUE
, aEffectiveValue
);
773 catch(const Exception
&)
781 OSL_FAIL("OFormattedModel::read : unknown version !");
782 // then the format of the aggregated set stay like it was during creation: void
783 defaultCommonEditProperties();
786 if ((nKey
!= -1) && m_xAggregateSet
.is())
788 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, Any(xSupplier
));
789 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, Any(nKey
));
793 setPropertyToDefault(PROPERTY_FORMATSSUPPLIER
);
794 setPropertyToDefault(PROPERTY_FORMATKEY
);
798 sal_uInt16
OFormattedModel::getPersistenceFlags() const
800 return (OEditBaseModel::getPersistenceFlags() & ~PF_HANDLE_COMMON_PROPS
);
801 // a) we do our own call to writeCommonEditProperties
804 bool OFormattedModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
806 Any
aControlValue( m_xAggregateFastSet
->getFastPropertyValue( getValuePropertyAggHandle() ) );
807 if ( aControlValue
== m_aSaveValue
)
810 // empty string + EmptyIsNull = void
811 if ( !aControlValue
.hasValue()
812 || ( ( aControlValue
.getValueTypeClass() == TypeClass_STRING
)
813 && getString( aControlValue
).isEmpty()
817 m_xColumnUpdate
->updateNull();
823 if ( aControlValue
.getValueTypeClass() == TypeClass_DOUBLE
|| (aControlValue
>>= f
)) // #i110323
825 DBTypeConversion::setValue( m_xColumnUpdate
, m_aNullDate
, getDouble( aControlValue
), m_nKeyType
);
829 DBG_ASSERT( aControlValue
.getValueTypeClass() == TypeClass_STRING
, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" );
830 m_xColumnUpdate
->updateString( getString( aControlValue
) );
833 catch(const Exception
&)
838 m_aSaveValue
= std::move(aControlValue
);
842 void OFormattedModel::onConnectedExternalValue( )
844 OEditBaseModel::onConnectedExternalValue();
845 updateFormatterNullDate();
848 Any
OFormattedModel::translateExternalValueToControlValue( const Any
& _rExternalValue
) const
851 switch( _rExternalValue
.getValueTypeClass() )
855 case TypeClass_STRING
:
856 aControlValue
= _rExternalValue
;
858 case TypeClass_BOOLEAN
:
860 bool bExternalValue
= false;
861 _rExternalValue
>>= bExternalValue
;
862 aControlValue
<<= static_cast<double>( bExternalValue
? 1 : 0 );
867 if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::Date
>::get() ) )
869 css::util::Date aDate
;
870 _rExternalValue
>>= aDate
;
871 aControlValue
<<= DBTypeConversion::toDouble( aDate
, m_aNullDate
);
873 else if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::Time
>::get() ) )
875 css::util::Time aTime
;
876 _rExternalValue
>>= aTime
;
877 aControlValue
<<= DBTypeConversion::toDouble( aTime
);
879 else if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::DateTime
>::get() ) )
881 css::util::DateTime aDateTime
;
882 _rExternalValue
>>= aDateTime
;
883 aControlValue
<<= DBTypeConversion::toDouble( aDateTime
, m_aNullDate
);
887 OSL_ENSURE( _rExternalValue
.getValueTypeClass() == TypeClass_DOUBLE
,
888 "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" );
890 OSL_VERIFY( _rExternalValue
>>= fValue
);
891 aControlValue
<<= fValue
;
895 return aControlValue
;
898 Any
OFormattedModel::translateControlValueToExternalValue( ) const
900 OSL_PRECOND( hasExternalValueBinding(),
901 "OFormattedModel::translateControlValueToExternalValue: precondition not met!" );
902 Any
aControlValue( getControlValue() );
903 if ( !aControlValue
.hasValue() )
904 return aControlValue
;
906 // translate into the external value type
907 Type
aExternalValueType( getExternalValueType() );
908 switch ( aExternalValueType
.getTypeClass() )
910 case TypeClass_STRING
:
913 if ( aControlValue
>>= sString
)
915 aExternalValue
<<= sString
;
920 case TypeClass_BOOLEAN
:
923 OSL_VERIFY( aControlValue
>>= fValue
);
924 // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
925 // and the control value is a string. This implies some weird misconfiguration
926 // of the FormattedModel, so we won't care for it for the moment.
927 aExternalValue
<<= fValue
!= 0.0;
933 OSL_VERIFY( aControlValue
>>= fValue
);
934 // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
935 // and the control value is a string. This implies some weird misconfiguration
936 // of the FormattedModel, so we won't care for it for the moment.
937 if ( aExternalValueType
.equals( cppu::UnoType
< css::util::Date
>::get() ) )
939 aExternalValue
<<= DBTypeConversion::toDate( fValue
, m_aNullDate
);
941 else if ( aExternalValueType
.equals( cppu::UnoType
< css::util::Time
>::get() ) )
943 aExternalValue
<<= DBTypeConversion::toTime( fValue
);
945 else if ( aExternalValueType
.equals( cppu::UnoType
< css::util::DateTime
>::get() ) )
947 aExternalValue
<<= DBTypeConversion::toDateTime( fValue
, m_aNullDate
);
951 OSL_ENSURE( aExternalValueType
.equals( cppu::UnoType
< double >::get() ),
952 "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" );
953 aExternalValue
<<= fValue
;
958 return aExternalValue
;
961 Any
OFormattedModel::translateDbColumnToControlValue()
964 m_aSaveValue
<<= DBTypeConversion::getValue( m_xColumn
, m_aNullDate
); // #100056# OJ
966 m_aSaveValue
<<= m_xColumn
->getString();
967 if ( m_xColumn
->wasNull() )
968 m_aSaveValue
.clear();
972 Sequence
< Type
> OFormattedModel::getSupportedBindingTypes()
974 ::std::vector
< Type
> aTypes
;
975 switch ( m_nKeyType
& ~NumberFormat::DEFINED
)
977 case NumberFormat::DATE
:
978 aTypes
.push_back(cppu::UnoType
< css::util::Date
>::get() );
980 case NumberFormat::TIME
:
981 aTypes
.push_back(cppu::UnoType
< css::util::Time
>::get() );
983 case NumberFormat::DATETIME
:
984 aTypes
.push_back(cppu::UnoType
< css::util::DateTime
>::get() );
986 case NumberFormat::TEXT
:
987 aTypes
.push_back(cppu::UnoType
< OUString
>::get() );
989 case NumberFormat::LOGICAL
:
990 aTypes
.push_back(cppu::UnoType
< sal_Bool
>::get() );
993 aTypes
.push_back( cppu::UnoType
< double >::get() );
994 return comphelper::containerToSequence(aTypes
);
997 Any
OFormattedModel::getDefaultForReset() const
999 return m_xAggregateSet
->getPropertyValue( PROPERTY_EFFECTIVE_DEFAULT
);
1002 void OFormattedModel::resetNoBroadcast()
1004 OEditBaseModel::resetNoBroadcast();
1005 m_aSaveValue
.clear();
1010 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1011 com_sun_star_form_OFormattedControl_get_implementation(css::uno::XComponentContext
* component
,
1012 css::uno::Sequence
<css::uno::Any
> const &)
1014 return cppu::acquire(new frm::OFormattedControl(component
));
1017 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */