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/zforlist.hxx>
32 #include <svl/numuno.hxx>
33 #include <vcl/keycodes.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/settings.hxx>
36 #include <tools/debug.hxx>
37 #include <i18nlangtag/languagetag.hxx>
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::sdbcx
;
63 using namespace css::beans
;
64 using namespace css::container
;
65 using namespace css::form
;
66 using namespace css::awt
;
67 using namespace css::io
;
68 using namespace css::lang
;
69 using namespace css::util
;
70 using namespace css::form::binding
;
76 class StandardFormatsSupplier
: public SvNumberFormatsSupplierObj
, public ::utl::ITerminationListener
79 std::unique_ptr
<SvNumberFormatter
> m_pMyPrivateFormatter
;
80 static WeakReference
< XNumberFormatsSupplier
> s_xDefaultFormatsSupplier
;
82 static Reference
< XNumberFormatsSupplier
> get( const Reference
< XComponentContext
>& _rxORB
);
84 StandardFormatsSupplier(const Reference
< XComponentContext
>& _rxFactory
,LanguageType _eSysLanguage
);
85 virtual ~StandardFormatsSupplier() override
;
87 virtual bool queryTermination() const override
;
88 virtual void notifyTermination() override
;
93 WeakReference
< XNumberFormatsSupplier
> StandardFormatsSupplier::s_xDefaultFormatsSupplier
;
94 StandardFormatsSupplier::StandardFormatsSupplier(const Reference
< XComponentContext
> & _rxContext
,LanguageType _eSysLanguage
)
95 :m_pMyPrivateFormatter(new SvNumberFormatter(_rxContext
, _eSysLanguage
))
97 SetNumberFormatter(m_pMyPrivateFormatter
.get());
99 ::utl::DesktopTerminationObserver::registerTerminationListener( this );
101 StandardFormatsSupplier::~StandardFormatsSupplier()
103 ::utl::DesktopTerminationObserver::revokeTerminationListener( this );
105 Reference
< XNumberFormatsSupplier
> StandardFormatsSupplier::get( const Reference
< XComponentContext
>& _rxORB
)
107 LanguageType eSysLanguage
= LANGUAGE_SYSTEM
;
109 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
110 Reference
< XNumberFormatsSupplier
> xSupplier
= s_xDefaultFormatsSupplier
;
111 if ( xSupplier
.is() )
113 // get the Office's locale
114 eSysLanguage
= SvtSysLocale().GetLanguageTag().getLanguageType( false);
116 rtl::Reference
<StandardFormatsSupplier
> pSupplier
= new StandardFormatsSupplier( _rxORB
, eSysLanguage
);
118 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
119 Reference
< XNumberFormatsSupplier
> xSupplier
= s_xDefaultFormatsSupplier
;
120 if ( xSupplier
.is() )
121 // somebody used the small time frame where the mutex was not locked to create and set
124 s_xDefaultFormatsSupplier
= pSupplier
;
128 bool StandardFormatsSupplier::queryTermination() const
132 void StandardFormatsSupplier::notifyTermination()
134 Reference
< XNumberFormatsSupplier
> xKeepAlive
= this;
135 // when the application is terminating, release our static reference so that we are cleared/destructed
136 // earlier than upon unloading the library
138 s_xDefaultFormatsSupplier
= WeakReference
< XNumberFormatsSupplier
>( );
139 SetNumberFormatter( nullptr );
140 m_pMyPrivateFormatter
.reset();
142 Sequence
<Type
> OFormattedControl::_getTypes()
144 return ::comphelper::concatSequences(
145 OFormattedControl_BASE::getTypes(),
146 OBoundControl::_getTypes()
149 Any SAL_CALL
OFormattedControl::queryAggregation(const Type
& _rType
)
151 Any aReturn
= OBoundControl::queryAggregation(_rType
);
152 if (!aReturn
.hasValue())
153 aReturn
= OFormattedControl_BASE::queryInterface(_rType
);
156 OFormattedControl::OFormattedControl(const Reference
<XComponentContext
>& _rxFactory
)
157 :OBoundControl(_rxFactory
, VCL_CONTROL_FORMATTEDFIELD
)
158 ,m_nKeyEvent(nullptr)
160 osl_atomic_increment(&m_refCount
);
162 Reference
<XWindow
> xComp
;
163 if (query_aggregation(m_xAggregate
, xComp
))
165 xComp
->addKeyListener(this);
168 osl_atomic_decrement(&m_refCount
);
170 OFormattedControl::~OFormattedControl()
173 Application::RemoveUserEvent( m_nKeyEvent
);
174 if (!OComponentHelper::rBHelper
.bDisposed
)
182 void OFormattedControl::disposing(const EventObject
& _rSource
)
184 OBoundControl::disposing(_rSource
);
186 void OFormattedControl::keyPressed(const css::awt::KeyEvent
& e
)
188 if( e
.KeyCode
!= KEY_RETURN
|| e
.Modifiers
!= 0 )
190 // Is the control located in a form with a Submit URL?
191 Reference
<css::beans::XPropertySet
> xSet(getModel(), UNO_QUERY
);
194 Reference
<XFormComponent
> xFComp(xSet
, UNO_QUERY
);
195 css::uno::Reference
<css::uno::XInterface
> xParent
= xFComp
->getParent();
198 Reference
<css::beans::XPropertySet
> xFormSet(xParent
, UNO_QUERY
);
201 Any
aTmp(xFormSet
->getPropertyValue( PROPERTY_TARGET_URL
));
202 if (!aTmp
.has
<OUString
>() ||
203 getString(aTmp
).isEmpty() )
205 Reference
<XIndexAccess
> xElements(xParent
, UNO_QUERY
);
206 sal_Int32 nCount
= xElements
->getCount();
209 Reference
<css::beans::XPropertySet
> xFCSet
;
210 for( sal_Int32 nIndex
=0; nIndex
< nCount
; nIndex
++ )
212 // Any aElement(xElements->getByIndex(nIndex));
213 xElements
->getByIndex(nIndex
) >>= xFCSet
;
214 if (hasProperty(PROPERTY_CLASSID
, xFCSet
) &&
215 getINT16(xFCSet
->getPropertyValue(PROPERTY_CLASSID
)) == FormComponentType::TEXTFIELD
)
217 // Found another Edit -> Do not submit then
223 // Because we're still in the Handler, execute submit asynchronously
225 Application::RemoveUserEvent( m_nKeyEvent
);
226 m_nKeyEvent
= Application::PostUserEvent( LINK(this, OFormattedControl
,
230 void OFormattedControl::keyReleased(const css::awt::KeyEvent
& /*e*/)
234 IMPL_LINK_NOARG(OFormattedControl
, OnKeyPressed
, void*, void)
236 m_nKeyEvent
= nullptr;
237 Reference
<XFormComponent
> xFComp(getModel(), UNO_QUERY
);
238 css::uno::Reference
<css::uno::XInterface
> xParent
= xFComp
->getParent();
239 Reference
<XSubmit
> xSubmit(xParent
, UNO_QUERY
);
241 xSubmit
->submit( Reference
<XControl
> (), css::awt::MouseEvent() );
244 css::uno::Sequence
<OUString
> OFormattedControl::getSupportedServiceNames()
246 css::uno::Sequence
<OUString
> aSupported
= OBoundControl::getSupportedServiceNames();
247 aSupported
.realloc(aSupported
.getLength() + 2);
248 OUString
*pArray
= aSupported
.getArray();
249 pArray
[aSupported
.getLength()-2] = FRM_SUN_CONTROL_FORMATTEDFIELD
;
250 pArray
[aSupported
.getLength()-1] = STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD
;
254 void OFormattedModel::implConstruct()
257 m_bOriginalNumeric
= false;
259 m_xOriginalFormatter
= nullptr;
260 m_nKeyType
= NumberFormat::UNDEFINED
;
261 m_aNullDate
= DBTypeConversion::getStandardDate();
262 // default our formats supplier
263 osl_atomic_increment(&m_refCount
);
264 setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
265 osl_atomic_decrement(&m_refCount
);
266 startAggregatePropertyListening( PROPERTY_FORMATKEY
);
267 startAggregatePropertyListening( PROPERTY_FORMATSSUPPLIER
);
269 OFormattedModel::OFormattedModel(const Reference
<XComponentContext
>& _rxFactory
)
270 :OEditBaseModel(_rxFactory
, VCL_CONTROLMODEL_FORMATTEDFIELD
, FRM_SUN_CONTROL_FORMATTEDFIELD
, true, true )
271 // use the old control name for compytibility reasons
272 ,OErrorBroadcaster( OComponentHelper::rBHelper
)
275 m_nClassId
= FormComponentType::TEXTFIELD
;
276 initValueProperty( PROPERTY_EFFECTIVE_VALUE
, PROPERTY_ID_EFFECTIVE_VALUE
);
278 OFormattedModel::OFormattedModel( const OFormattedModel
* _pOriginal
, const Reference
< XComponentContext
>& _rxFactory
)
279 :OEditBaseModel( _pOriginal
, _rxFactory
)
280 ,OErrorBroadcaster( OComponentHelper::rBHelper
)
285 OFormattedModel::~OFormattedModel()
290 css::uno::Reference
< css::util::XCloneable
> SAL_CALL
OFormattedModel::createClone()
292 rtl::Reference
<OFormattedModel
> pClone
= new OFormattedModel(this, getContext());
293 pClone
->clonedFrom(this);
297 void SAL_CALL
OFormattedModel::disposing()
299 OErrorBroadcaster::disposing();
300 OEditBaseModel::disposing();
304 css::uno::Sequence
<OUString
> OFormattedModel::getSupportedServiceNames()
306 css::uno::Sequence
<OUString
> aSupported
= OEditBaseModel::getSupportedServiceNames();
307 sal_Int32 nOldLen
= aSupported
.getLength();
308 aSupported
.realloc( nOldLen
+ 9 );
309 OUString
* pStoreTo
= aSupported
.getArray() + nOldLen
;
310 *pStoreTo
++ = BINDABLE_CONTROL_MODEL
;
311 *pStoreTo
++ = DATA_AWARE_CONTROL_MODEL
;
312 *pStoreTo
++ = VALIDATABLE_CONTROL_MODEL
;
313 *pStoreTo
++ = BINDABLE_DATA_AWARE_CONTROL_MODEL
;
314 *pStoreTo
++ = VALIDATABLE_BINDABLE_CONTROL_MODEL
;
315 *pStoreTo
++ = FRM_SUN_COMPONENT_FORMATTEDFIELD
;
316 *pStoreTo
++ = FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD
;
317 *pStoreTo
++ = BINDABLE_DATABASE_FORMATTED_FIELD
;
318 *pStoreTo
++ = FRM_COMPONENT_FORMATTEDFIELD
;
323 Any SAL_CALL
OFormattedModel::queryAggregation(const Type
& _rType
)
325 Any aReturn
= OEditBaseModel::queryAggregation( _rType
);
326 return aReturn
.hasValue() ? aReturn
: OErrorBroadcaster::queryInterface( _rType
);
330 Sequence
< Type
> OFormattedModel::_getTypes()
332 return ::comphelper::concatSequences(
333 OEditBaseModel::_getTypes(),
334 OErrorBroadcaster::getTypes()
339 OUString SAL_CALL
OFormattedModel::getServiceName()
341 return FRM_COMPONENT_EDIT
;
345 void OFormattedModel::describeFixedProperties( Sequence
< Property
>& _rProps
) const
347 OEditBaseModel::describeFixedProperties( _rProps
);
348 sal_Int32 nOldCount
= _rProps
.getLength();
349 _rProps
.realloc( nOldCount
+ 3);
350 css::beans::Property
* pProperties
= _rProps
.getArray() + nOldCount
;
351 *pProperties
++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL
, PROPERTY_ID_EMPTY_IS_NULL
, cppu::UnoType
<bool>::get(),
352 css::beans::PropertyAttribute::BOUND
);
353 *pProperties
++ = css::beans::Property(PROPERTY_TABINDEX
, PROPERTY_ID_TABINDEX
, cppu::UnoType
<sal_Int16
>::get(), css::beans::PropertyAttribute::BOUND
);
354 *pProperties
++ = css::beans::Property(PROPERTY_FILTERPROPOSAL
, PROPERTY_ID_FILTERPROPOSAL
, cppu::UnoType
<bool>::get(),
355 css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::MAYBEDEFAULT
);
356 DBG_ASSERT( pProperties
== _rProps
.getArray() + _rProps
.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
359 void OFormattedModel::describeAggregateProperties( Sequence
< Property
>& _rAggregateProps
) const
361 OEditBaseModel::describeAggregateProperties( _rAggregateProps
);
362 // TreatAsNumeric is not transient: we want to attach it to the UI
363 // This is necessary to make EffectiveDefault (which may be text or a number) meaningful
364 ModifyPropertyAttributes(_rAggregateProps
, PROPERTY_TREATASNUMERIC
, 0, PropertyAttribute::TRANSIENT
);
365 // Same for FormatKey
366 // (though the paragraph above for the TreatAsNumeric does not hold anymore - we do not have an UI for this.
367 // But we have for the format key ...)
368 ModifyPropertyAttributes(_rAggregateProps
, PROPERTY_FORMATKEY
, 0, PropertyAttribute::TRANSIENT
);
369 RemoveProperty(_rAggregateProps
, PROPERTY_STRICTFORMAT
);
370 // no strict format property for formatted fields: it does not make sense, 'cause
371 // there is no general way to decide which characters/sub strings are allowed during the input of an
372 // arbitrary formatted control
375 void OFormattedModel::setPropertyToDefaultByHandle(sal_Int32 nHandle
)
377 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
379 Reference
<XNumberFormatsSupplier
> xSupplier
= calcDefaultFormatsSupplier();
380 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::setPropertyToDefaultByHandle(FORMATSSUPPLIER) : have no aggregate !");
381 if (m_xAggregateSet
.is())
382 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(xSupplier
));
385 OEditBaseModel::setPropertyToDefaultByHandle(nHandle
);
388 void OFormattedModel::setPropertyToDefault(const OUString
& aPropertyName
)
390 OPropertyArrayAggregationHelper
& rPH
= m_aPropertyBagHelper
.getInfoHelper();
391 sal_Int32 nHandle
= rPH
.getHandleByName( aPropertyName
);
392 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
393 setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
395 OEditBaseModel::setPropertyToDefault(aPropertyName
);
398 Any
OFormattedModel::getPropertyDefaultByHandle( sal_Int32 nHandle
) const
400 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
402 Reference
<XNumberFormatsSupplier
> xSupplier
= calcDefaultFormatsSupplier();
403 return makeAny(xSupplier
);
406 return OEditBaseModel::getPropertyDefaultByHandle(nHandle
);
409 Any SAL_CALL
OFormattedModel::getPropertyDefault( const OUString
& aPropertyName
)
411 OPropertyArrayAggregationHelper
& rPH
= m_aPropertyBagHelper
.getInfoHelper();
412 sal_Int32 nHandle
= rPH
.getHandleByName( aPropertyName
);
413 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
414 return getPropertyDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
416 return OEditBaseModel::getPropertyDefault(aPropertyName
);
419 void OFormattedModel::_propertyChanged( const css::beans::PropertyChangeEvent
& evt
)
421 // TODO: check how this works with external bindings
422 OSL_ENSURE( evt
.Source
== m_xAggregateSet
, "OFormattedModel::_propertyChanged: where did this come from?" );
423 if ( evt
.Source
!= m_xAggregateSet
)
426 if ( evt
.PropertyName
== PROPERTY_FORMATKEY
)
428 if ( evt
.NewValue
.getValueType().getTypeClass() == TypeClass_LONG
)
432 ::osl::MutexGuard
aGuard( m_aMutex
);
433 Reference
<XNumberFormatsSupplier
> xSupplier( calcFormatsSupplier() );
434 m_nKeyType
= getNumberFormatType(xSupplier
->getNumberFormats(), getINT32( evt
.NewValue
) );
435 // as m_aSaveValue (which is used by commitControlValueToDbColumn) is format dependent we have
436 // to recalc it, which is done by translateDbColumnToControlValue
437 if ( m_xColumn
.is() && m_xAggregateFastSet
.is() && !m_xCursor
->isBeforeFirst() && !m_xCursor
->isAfterLast())
439 setControlValue( translateDbColumnToControlValue(), eOther
);
441 // if we're connected to an external value binding, then re-calculate the type
442 // used to exchange the value - it depends on the format, too
443 if ( hasExternalValueBinding() )
445 calculateExternalValueType();
448 catch(const Exception
&)
454 if ( evt
.PropertyName
== PROPERTY_FORMATSSUPPLIER
)
456 updateFormatterNullDate();
459 OBoundControlModel::_propertyChanged( evt
);
462 void OFormattedModel::updateFormatterNullDate()
464 // calc the current NULL date
465 Reference
< XNumberFormatsSupplier
> xSupplier( calcFormatsSupplier() );
466 if ( xSupplier
.is() )
467 xSupplier
->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate
;
470 Reference
< XNumberFormatsSupplier
> OFormattedModel::calcFormatsSupplier() const
472 Reference
<XNumberFormatsSupplier
> xSupplier
;
473 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::calcFormatsSupplier : have no aggregate !");
474 // Does my aggregate model have a FormatSupplier?
475 if( m_xAggregateSet
.is() )
476 m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
) >>= xSupplier
;
478 // check if my parent form has a supplier
479 xSupplier
= calcFormFormatsSupplier();
481 xSupplier
= calcDefaultFormatsSupplier();
482 DBG_ASSERT(xSupplier
.is(), "OFormattedModel::calcFormatsSupplier : no supplier !");
483 // We should have one by now
487 Reference
<XNumberFormatsSupplier
> OFormattedModel::calcFormFormatsSupplier() const
489 Reference
<XChild
> xMe(const_cast<OFormattedModel
*>(this));
490 // By this we make sure that we get the right object even when aggregating
491 DBG_ASSERT(xMe
.is(), "OFormattedModel::calcFormFormatsSupplier : I should have a content interface !");
492 // Iterate through until we reach a StartForm (starting with an own Parent)
493 Reference
<XChild
> xParent(xMe
->getParent(), UNO_QUERY
);
494 Reference
<XForm
> xNextParentForm(xParent
, UNO_QUERY
);
495 while (!xNextParentForm
.is() && xParent
.is())
497 xParent
.set(xParent
->getParent(), css::uno::UNO_QUERY
);
498 xNextParentForm
.set(xParent
, css::uno::UNO_QUERY
);
500 if (!xNextParentForm
.is())
502 OSL_FAIL("OFormattedModel::calcFormFormatsSupplier : have no ancestor which is a form !");
505 // The FormatSupplier of my ancestor (if it has one)
506 Reference
< XRowSet
> xRowSet( xNextParentForm
, UNO_QUERY
);
507 Reference
< XNumberFormatsSupplier
> xSupplier
;
509 xSupplier
= getNumberFormats( getConnection(xRowSet
), true, getContext() );
513 Reference
< XNumberFormatsSupplier
> OFormattedModel::calcDefaultFormatsSupplier() const
515 return StandardFormatsSupplier::get( getContext() );
519 void OFormattedModel::loaded(const EventObject
& rEvent
)
521 // HACK: our onConnectedDbColumn accesses our NumberFormatter which locks the solar mutex (as it doesn't have
522 // an own one). To prevent deadlocks with other threads which may request a property from us in an
523 // UI-triggered action (e.g. a tooltip) we lock the solar mutex _here_ before our base class locks
524 // its own mutex (which is used for property requests)
525 // alternative a): we use two mutexes, one which is passed to the OPropertysetHelper and used for
526 // property requests and one for our own code. This would need a lot of code rewriting
527 // alternative b): The NumberFormatter has to be really threadsafe (with an own mutex), which is
528 // the only "clean" solution for me.
529 SolarMutexGuard aGuard
;
530 OEditBaseModel::loaded(rEvent
);
533 void OFormattedModel::onConnectedDbColumn( const Reference
< XInterface
>& _rxForm
)
535 m_xOriginalFormatter
= nullptr;
536 // get some properties of the field
537 Reference
<XPropertySet
> xField
= getField();
538 sal_Int32 nFormatKey
= 0;
539 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::onConnectedDbColumn : have no aggregate !");
540 if (m_xAggregateSet
.is())
541 { // all the following doesn't make any sense if we have no aggregate ...
542 Any aSupplier
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
);
543 DBG_ASSERT( aSupplier
.hasValue(), "OFormattedModel::onConnectedDbColumn : invalid property value !" );
544 // This should've been set to the correct value in the ctor or in the read
545 Any aFmtKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
546 if ( !(aFmtKey
>>= nFormatKey
) )
547 { // nobody gave us a format to use. So we examine the field we're bound to for a
548 // format key, and use it ourself, too
549 sal_Int32 nType
= DataType::VARCHAR
;
552 aFmtKey
= xField
->getPropertyValue(PROPERTY_FORMATKEY
);
553 xField
->getPropertyValue(PROPERTY_FIELDTYPE
) >>= nType
;
555 Reference
<XNumberFormatsSupplier
> xSupplier
= calcFormFormatsSupplier();
556 DBG_ASSERT(xSupplier
.is(), "OFormattedModel::onConnectedDbColumn : bound to a field but no parent with a formatter ? how this ?");
559 m_bOriginalNumeric
= getBOOL(getPropertyValue(PROPERTY_TREATASNUMERIC
));
560 if (!aFmtKey
.hasValue())
561 { // we aren't bound to a field (or this field's format is invalid)
562 // -> determine the standard text (or numeric) format of the supplier
563 Reference
<XNumberFormatTypes
> xTypes(xSupplier
->getNumberFormats(), UNO_QUERY
);
566 Locale aApplicationLocale
= Application::GetSettings().GetUILanguageTag().getLocale();
567 if (m_bOriginalNumeric
)
568 aFmtKey
<<= xTypes
->getStandardFormat(NumberFormat::NUMBER
, aApplicationLocale
);
570 aFmtKey
<<= xTypes
->getStandardFormat(NumberFormat::TEXT
, aApplicationLocale
);
573 aSupplier
>>= m_xOriginalFormatter
;
574 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(xSupplier
));
575 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, aFmtKey
);
576 // Adapt the NumericFalg to my bound field
583 case DataType::BOOLEAN
:
584 case DataType::TINYINT
:
585 case DataType::SMALLINT
:
586 case DataType::INTEGER
:
587 case DataType::BIGINT
:
588 case DataType::FLOAT
:
590 case DataType::DOUBLE
:
591 case DataType::NUMERIC
:
592 case DataType::DECIMAL
:
595 case DataType::TIMESTAMP
:
601 m_bNumeric
= m_bOriginalNumeric
;
602 setPropertyValue(PROPERTY_TREATASNUMERIC
, makeAny(m_bNumeric
));
603 OSL_VERIFY( aFmtKey
>>= nFormatKey
);
607 Reference
<XNumberFormatsSupplier
> xSupplier
= calcFormatsSupplier();
608 m_bNumeric
= getBOOL( getPropertyValue( PROPERTY_TREATASNUMERIC
) );
609 m_nKeyType
= getNumberFormatType( xSupplier
->getNumberFormats(), nFormatKey
);
610 xSupplier
->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate
;
611 OEditBaseModel::onConnectedDbColumn( _rxForm
);
614 void OFormattedModel::onDisconnectedDbColumn()
616 OEditBaseModel::onDisconnectedDbColumn();
617 if (m_xOriginalFormatter
.is())
618 { // Our aggregated model does not hold any Format information
619 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(m_xOriginalFormatter
));
620 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, Any());
621 setPropertyValue(PROPERTY_TREATASNUMERIC
, makeAny(m_bOriginalNumeric
));
622 m_xOriginalFormatter
= nullptr;
624 m_nKeyType
= NumberFormat::UNDEFINED
;
625 m_aNullDate
= DBTypeConversion::getStandardDate();
628 void OFormattedModel::write(const Reference
<XObjectOutputStream
>& _rxOutStream
)
630 OEditBaseModel::write(_rxOutStream
);
631 _rxOutStream
->writeShort(0x0003);
632 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::write : have no aggregate !");
633 // Bring my Format (may be void) to a persistent Format.
634 // The Supplier together with the Key is already persistent, but that doesn't mean
635 // we have to save the Supplier (which would be quite some overhead)
636 Reference
<XNumberFormatsSupplier
> xSupplier
;
638 bool bVoidKey
= true;
639 if (m_xAggregateSet
.is())
641 Any aSupplier
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
);
642 if (aSupplier
.getValueType().getTypeClass() != TypeClass_VOID
)
644 OSL_VERIFY( aSupplier
>>= xSupplier
);
646 aFmtKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
647 bVoidKey
= (!xSupplier
.is() || !aFmtKey
.hasValue()) || (isLoaded() && m_xOriginalFormatter
.is());
648 // (no Format and/or Key) OR (loaded and faked Formatter)
650 _rxOutStream
->writeBoolean(!bVoidKey
);
653 // Create persistent values from the FormatKey and the Formatter
654 Any aKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
655 sal_Int32 nKey
= aKey
.hasValue() ? getINT32(aKey
) : 0;
656 Reference
<XNumberFormats
> xFormats
= xSupplier
->getNumberFormats();
657 OUString sFormatDescription
;
658 LanguageType eFormatLanguage
= LANGUAGE_DONTKNOW
;
659 static constexpr OUStringLiteral s_aLocaleProp
= u
"Locale";
660 Reference
<css::beans::XPropertySet
> xFormat
= xFormats
->getByKey(nKey
);
661 if (hasProperty(s_aLocaleProp
, xFormat
))
663 Any aLocale
= xFormat
->getPropertyValue(s_aLocaleProp
);
664 DBG_ASSERT(aLocale
.has
<Locale
>(), "OFormattedModel::write : invalid language property !");
665 if (auto pLocale
= o3tl::tryAccess
<Locale
>(aLocale
))
667 eFormatLanguage
= LanguageTag::convertToLanguageType( *pLocale
, false);
670 static constexpr OUStringLiteral s_aFormatStringProp
= u
"FormatString";
671 if (hasProperty(s_aFormatStringProp
, xFormat
))
672 xFormat
->getPropertyValue(s_aFormatStringProp
) >>= sFormatDescription
;
673 _rxOutStream
->writeUTF(sFormatDescription
);
674 _rxOutStream
->writeLong(static_cast<sal_uInt16
>(eFormatLanguage
));
676 // version 2 : write the properties common to all OEditBaseModels
677 writeCommonEditProperties(_rxOutStream
);
678 // version 3 : write the effective value property of the aggregate
679 // Due to a bug within the UnoControlFormattedFieldModel implementation (our default aggregate)
680 // this props value isn't correctly read and this can't be corrected without being incompatible.
681 // so we have our own handling.
682 // and to be a little bit more compatible we make the following section skippable
684 OStreamSection
aDownCompat(_rxOutStream
);
685 // a sub version within the skippable block
686 _rxOutStream
->writeShort(0x0000);
687 // version 0: the effective value of the aggregate
689 if (m_xAggregateSet
.is())
691 try { aEffectiveValue
= m_xAggregateSet
->getPropertyValue(PROPERTY_EFFECTIVE_VALUE
); } catch(const Exception
&) { }
694 OStreamSection
aDownCompat2(_rxOutStream
);
695 switch (aEffectiveValue
.getValueType().getTypeClass())
697 case TypeClass_STRING
:
698 _rxOutStream
->writeShort(0x0000);
699 _rxOutStream
->writeUTF(::comphelper::getString(aEffectiveValue
));
701 case TypeClass_DOUBLE
:
702 _rxOutStream
->writeShort(0x0001);
703 _rxOutStream
->writeDouble(::comphelper::getDouble(aEffectiveValue
));
705 default: // void and all unknown states
706 DBG_ASSERT(!aEffectiveValue
.hasValue(), "FmXFormattedModel::write : unknown property value type !");
707 _rxOutStream
->writeShort(0x0002);
714 void OFormattedModel::read(const Reference
<XObjectInputStream
>& _rxInStream
)
716 OEditBaseModel::read(_rxInStream
);
717 sal_uInt16 nVersion
= _rxInStream
->readShort();
718 Reference
<XNumberFormatsSupplier
> xSupplier
;
726 bool bNonVoidKey
= _rxInStream
->readBoolean();
729 // read string and language...
730 OUString sFormatDescription
= _rxInStream
->readUTF();
731 LanguageType
eDescriptionLanguage(_rxInStream
->readLong());
732 // and let a formatter roll dice based on that to create a key...
733 xSupplier
= calcFormatsSupplier();
734 // calcFormatsSupplier first takes the one from the model, then one from the starform, then a new one...
735 Reference
<XNumberFormats
> xFormats
= xSupplier
->getNumberFormats();
738 Locale
aDescriptionLanguage( LanguageTag::convertToLocale(eDescriptionLanguage
));
739 nKey
= xFormats
->queryKey(sFormatDescription
, aDescriptionLanguage
, false);
740 if (nKey
== sal_Int32(-1))
741 { // does not yet exist in my formatter...
742 nKey
= xFormats
->addNew(sFormatDescription
, aDescriptionLanguage
);
746 if ((nVersion
== 0x0002) || (nVersion
== 0x0003))
747 readCommonEditProperties(_rxInStream
);
748 if (nVersion
== 0x0003)
749 { // since version 3 there is a "skippable" block at this position
750 OStreamSection
aDownCompat(_rxInStream
);
751 _rxInStream
->readShort(); // sub-version
752 // version 0 and higher: the "effective value" property
755 OStreamSection
aDownCompat2(_rxInStream
);
756 switch (_rxInStream
->readShort())
759 aEffectiveValue
<<= _rxInStream
->readUTF();
762 aEffectiveValue
<<= _rxInStream
->readDouble();
767 OSL_FAIL("FmXFormattedModel::read : unknown effective value type!");
770 // this property is only to be set if we have no control source: in all other cases the base class made a
771 // reset after it's read and this set the effective value to a default value
772 if ( m_xAggregateSet
.is() && getControlSource().isEmpty() )
776 m_xAggregateSet
->setPropertyValue(PROPERTY_EFFECTIVE_VALUE
, aEffectiveValue
);
778 catch(const Exception
&)
786 OSL_FAIL("OFormattedModel::read : unknown version !");
787 // then the format of the aggregated set stay like it was during creation: void
788 defaultCommonEditProperties();
791 if ((nKey
!= -1) && m_xAggregateSet
.is())
793 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(xSupplier
));
794 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, makeAny(nKey
));
798 setPropertyToDefault(PROPERTY_FORMATSSUPPLIER
);
799 setPropertyToDefault(PROPERTY_FORMATKEY
);
803 sal_uInt16
OFormattedModel::getPersistenceFlags() const
805 return (OEditBaseModel::getPersistenceFlags() & ~PF_HANDLE_COMMON_PROPS
);
806 // a) we do our own call to writeCommonEditProperties
809 bool OFormattedModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
811 Any
aControlValue( m_xAggregateFastSet
->getFastPropertyValue( getValuePropertyAggHandle() ) );
812 if ( aControlValue
!= m_aSaveValue
)
814 // empty string + EmptyIsNull = void
815 if ( !aControlValue
.hasValue()
816 || ( ( aControlValue
.getValueType().getTypeClass() == TypeClass_STRING
)
817 && getString( aControlValue
).isEmpty()
821 m_xColumnUpdate
->updateNull();
827 if ( aControlValue
.getValueType().getTypeClass() == TypeClass_DOUBLE
|| (aControlValue
>>= f
)) // #i110323
829 DBTypeConversion::setValue( m_xColumnUpdate
, m_aNullDate
, getDouble( aControlValue
), m_nKeyType
);
833 DBG_ASSERT( aControlValue
.getValueType().getTypeClass() == TypeClass_STRING
, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" );
834 m_xColumnUpdate
->updateString( getString( aControlValue
) );
837 catch(const Exception
&)
842 m_aSaveValue
= aControlValue
;
847 void OFormattedModel::onConnectedExternalValue( )
849 OEditBaseModel::onConnectedExternalValue();
850 updateFormatterNullDate();
853 Any
OFormattedModel::translateExternalValueToControlValue( const Any
& _rExternalValue
) const
856 switch( _rExternalValue
.getValueTypeClass() )
860 case TypeClass_STRING
:
861 aControlValue
= _rExternalValue
;
863 case TypeClass_BOOLEAN
:
865 bool bExternalValue
= false;
866 _rExternalValue
>>= bExternalValue
;
867 aControlValue
<<= static_cast<double>( bExternalValue
? 1 : 0 );
872 if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::Date
>::get() ) )
874 css::util::Date aDate
;
875 _rExternalValue
>>= aDate
;
876 aControlValue
<<= DBTypeConversion::toDouble( aDate
, m_aNullDate
);
878 else if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::Time
>::get() ) )
880 css::util::Time aTime
;
881 _rExternalValue
>>= aTime
;
882 aControlValue
<<= DBTypeConversion::toDouble( aTime
);
884 else if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::DateTime
>::get() ) )
886 css::util::DateTime aDateTime
;
887 _rExternalValue
>>= aDateTime
;
888 aControlValue
<<= DBTypeConversion::toDouble( aDateTime
, m_aNullDate
);
892 OSL_ENSURE( _rExternalValue
.getValueTypeClass() == TypeClass_DOUBLE
,
893 "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" );
895 OSL_VERIFY( _rExternalValue
>>= fValue
);
896 aControlValue
<<= fValue
;
900 return aControlValue
;
903 Any
OFormattedModel::translateControlValueToExternalValue( ) const
905 OSL_PRECOND( hasExternalValueBinding(),
906 "OFormattedModel::translateControlValueToExternalValue: precondition not met!" );
907 Any
aControlValue( getControlValue() );
908 if ( !aControlValue
.hasValue() )
909 return aControlValue
;
911 // translate into the external value type
912 Type
aExternalValueType( getExternalValueType() );
913 switch ( aExternalValueType
.getTypeClass() )
915 case TypeClass_STRING
:
918 if ( aControlValue
>>= sString
)
920 aExternalValue
<<= sString
;
925 case TypeClass_BOOLEAN
:
928 OSL_VERIFY( aControlValue
>>= fValue
);
929 // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
930 // and the control value is a string. This implies some weird misconfiguration
931 // of the FormattedModel, so we won't care for it for the moment.
932 aExternalValue
<<= fValue
!= 0.0;
938 OSL_VERIFY( aControlValue
>>= fValue
);
939 // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
940 // and the control value is a string. This implies some weird misconfiguration
941 // of the FormattedModel, so we won't care for it for the moment.
942 if ( aExternalValueType
.equals( cppu::UnoType
< css::util::Date
>::get() ) )
944 aExternalValue
<<= DBTypeConversion::toDate( fValue
, m_aNullDate
);
946 else if ( aExternalValueType
.equals( cppu::UnoType
< css::util::Time
>::get() ) )
948 aExternalValue
<<= DBTypeConversion::toTime( fValue
);
950 else if ( aExternalValueType
.equals( cppu::UnoType
< css::util::DateTime
>::get() ) )
952 aExternalValue
<<= DBTypeConversion::toDateTime( fValue
, m_aNullDate
);
956 OSL_ENSURE( aExternalValueType
.equals( cppu::UnoType
< double >::get() ),
957 "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" );
958 aExternalValue
<<= fValue
;
963 return aExternalValue
;
966 Any
OFormattedModel::translateDbColumnToControlValue()
969 m_aSaveValue
<<= DBTypeConversion::getValue( m_xColumn
, m_aNullDate
); // #100056# OJ
971 m_aSaveValue
<<= m_xColumn
->getString();
972 if ( m_xColumn
->wasNull() )
973 m_aSaveValue
.clear();
977 Sequence
< Type
> OFormattedModel::getSupportedBindingTypes()
979 ::std::vector
< Type
> aTypes
;
980 switch ( m_nKeyType
& ~NumberFormat::DEFINED
)
982 case NumberFormat::DATE
:
983 aTypes
.push_back(cppu::UnoType
< css::util::Date
>::get() );
985 case NumberFormat::TIME
:
986 aTypes
.push_back(cppu::UnoType
< css::util::Time
>::get() );
988 case NumberFormat::DATETIME
:
989 aTypes
.push_back(cppu::UnoType
< css::util::DateTime
>::get() );
991 case NumberFormat::TEXT
:
992 aTypes
.push_back(cppu::UnoType
< OUString
>::get() );
994 case NumberFormat::LOGICAL
:
995 aTypes
.push_back(cppu::UnoType
< sal_Bool
>::get() );
998 aTypes
.push_back( cppu::UnoType
< double >::get() );
999 return comphelper::containerToSequence(aTypes
);
1002 Any
OFormattedModel::getDefaultForReset() const
1004 return m_xAggregateSet
->getPropertyValue( PROPERTY_EFFECTIVE_DEFAULT
);
1007 void OFormattedModel::resetNoBroadcast()
1009 OEditBaseModel::resetNoBroadcast();
1010 m_aSaveValue
.clear();
1015 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1016 com_sun_star_form_OFormattedControl_get_implementation(css::uno::XComponentContext
* component
,
1017 css::uno::Sequence
<css::uno::Any
> const &)
1019 return cppu::acquire(new frm::OFormattedControl(component
));
1022 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */