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/zforlist.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/sdbc/DataType.hpp>
38 #include <com/sun/star/util/NumberFormat.hpp>
39 #include <com/sun/star/util/Date.hpp>
40 #include <com/sun/star/util/Time.hpp>
41 #include <com/sun/star/awt/MouseEvent.hpp>
42 #include <com/sun/star/form/XSubmit.hpp>
43 #include <com/sun/star/awt/XWindow.hpp>
44 #include <com/sun/star/form/FormComponentType.hpp>
45 #include <com/sun/star/util/XNumberFormatTypes.hpp>
46 #include <com/sun/star/form/XForm.hpp>
47 #include <com/sun/star/container/XIndexAccess.hpp>
48 #include <osl/mutex.hxx>
49 // needed as long as we use the SolarMutex
50 #include <comphelper/streamsection.hxx>
51 #include <cppuhelper/weakref.hxx>
52 #include <unotools/desktopterminationobserver.hxx>
57 using namespace dbtools
;
58 using namespace css::uno
;
59 using namespace css::sdb
;
60 using namespace css::sdbc
;
61 using namespace css::sdbcx
;
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
;
69 using namespace css::form::binding
;
75 class StandardFormatsSupplier
: protected SvNumberFormatsSupplierObj
, public ::utl::ITerminationListener
78 std::unique_ptr
<SvNumberFormatter
> m_pMyPrivateFormatter
;
79 static WeakReference
< XNumberFormatsSupplier
> s_xDefaultFormatsSupplier
;
81 static Reference
< XNumberFormatsSupplier
> get( const Reference
< XComponentContext
>& _rxORB
);
83 StandardFormatsSupplier(const Reference
< XComponentContext
>& _rxFactory
,LanguageType _eSysLanguage
);
84 virtual ~StandardFormatsSupplier() override
;
86 virtual bool queryTermination() const override
;
87 virtual void notifyTermination() override
;
92 WeakReference
< XNumberFormatsSupplier
> StandardFormatsSupplier::s_xDefaultFormatsSupplier
;
93 StandardFormatsSupplier::StandardFormatsSupplier(const Reference
< XComponentContext
> & _rxContext
,LanguageType _eSysLanguage
)
94 :SvNumberFormatsSupplierObj()
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 StandardFormatsSupplier
* pSupplier
= new StandardFormatsSupplier( _rxORB
, eSysLanguage
);
117 Reference
< XNumberFormatsSupplier
> xNewlyCreatedSupplier( pSupplier
);
119 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
120 Reference
< XNumberFormatsSupplier
> xSupplier
= s_xDefaultFormatsSupplier
;
121 if ( xSupplier
.is() )
122 // somebody used the small time frame where the mutex was not locked to create and set
125 s_xDefaultFormatsSupplier
= xNewlyCreatedSupplier
;
127 return xNewlyCreatedSupplier
;
129 bool StandardFormatsSupplier::queryTermination() const
133 void StandardFormatsSupplier::notifyTermination()
135 Reference
< XNumberFormatsSupplier
> xKeepAlive
= this;
136 // when the application is terminating, release our static reference so that we are cleared/destructed
137 // earlier than upon unloading the library
139 s_xDefaultFormatsSupplier
= WeakReference
< XNumberFormatsSupplier
>( );
140 SetNumberFormatter( nullptr );
141 m_pMyPrivateFormatter
.reset();
143 Sequence
<Type
> OFormattedControl::_getTypes()
145 return ::comphelper::concatSequences(
146 OFormattedControl_BASE::getTypes(),
147 OBoundControl::_getTypes()
150 Any SAL_CALL
OFormattedControl::queryAggregation(const Type
& _rType
)
152 Any aReturn
= OBoundControl::queryAggregation(_rType
);
153 if (!aReturn
.hasValue())
154 aReturn
= OFormattedControl_BASE::queryInterface(_rType
);
157 OFormattedControl::OFormattedControl(const Reference
<XComponentContext
>& _rxFactory
)
158 :OBoundControl(_rxFactory
, VCL_CONTROL_FORMATTEDFIELD
)
159 ,m_nKeyEvent(nullptr)
161 osl_atomic_increment(&m_refCount
);
163 Reference
<XWindow
> xComp
;
164 if (query_aggregation(m_xAggregate
, xComp
))
166 xComp
->addKeyListener(this);
169 osl_atomic_decrement(&m_refCount
);
171 OFormattedControl::~OFormattedControl()
174 Application::RemoveUserEvent( m_nKeyEvent
);
175 if (!OComponentHelper::rBHelper
.bDisposed
)
183 void OFormattedControl::disposing(const EventObject
& _rSource
)
185 OBoundControl::disposing(_rSource
);
187 void OFormattedControl::keyPressed(const css::awt::KeyEvent
& e
)
189 if( e
.KeyCode
!= KEY_RETURN
|| e
.Modifiers
!= 0 )
191 // Is the control located in a form with a Submit URL?
192 Reference
<css::beans::XPropertySet
> xSet(getModel(), UNO_QUERY
);
195 Reference
<XFormComponent
> xFComp(xSet
, UNO_QUERY
);
196 css::uno::Reference
<css::uno::XInterface
> xParent
= xFComp
->getParent();
199 Reference
<css::beans::XPropertySet
> xFormSet(xParent
, UNO_QUERY
);
202 Any
aTmp(xFormSet
->getPropertyValue( PROPERTY_TARGET_URL
));
203 if (!aTmp
.has
<OUString
>() ||
204 getString(aTmp
).isEmpty() )
206 Reference
<XIndexAccess
> xElements(xParent
, UNO_QUERY
);
207 sal_Int32 nCount
= xElements
->getCount();
210 Reference
<css::beans::XPropertySet
> xFCSet
;
211 for( sal_Int32 nIndex
=0; nIndex
< nCount
; nIndex
++ )
213 // Any aElement(xElements->getByIndex(nIndex));
214 xElements
->getByIndex(nIndex
) >>= xFCSet
;
215 if (hasProperty(PROPERTY_CLASSID
, xFCSet
) &&
216 getINT16(xFCSet
->getPropertyValue(PROPERTY_CLASSID
)) == FormComponentType::TEXTFIELD
)
218 // Found another Edit -> Do not submit then
224 // Because we're still in the Handler, execute submit asynchronously
226 Application::RemoveUserEvent( m_nKeyEvent
);
227 m_nKeyEvent
= Application::PostUserEvent( LINK(this, OFormattedControl
,
231 void OFormattedControl::keyReleased(const css::awt::KeyEvent
& /*e*/)
235 IMPL_LINK_NOARG(OFormattedControl
, OnKeyPressed
, void*, void)
237 m_nKeyEvent
= nullptr;
238 Reference
<XFormComponent
> xFComp(getModel(), UNO_QUERY
);
239 css::uno::Reference
<css::uno::XInterface
> xParent
= xFComp
->getParent();
240 Reference
<XSubmit
> xSubmit(xParent
, UNO_QUERY
);
242 xSubmit
->submit( Reference
<XControl
> (), css::awt::MouseEvent() );
245 css::uno::Sequence
<OUString
> OFormattedControl::getSupportedServiceNames()
247 css::uno::Sequence
<OUString
> aSupported
= OBoundControl::getSupportedServiceNames();
248 aSupported
.realloc(aSupported
.getLength() + 2);
249 OUString
*pArray
= aSupported
.getArray();
250 pArray
[aSupported
.getLength()-2] = FRM_SUN_CONTROL_FORMATTEDFIELD
;
251 pArray
[aSupported
.getLength()-1] = STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD
;
255 void OFormattedModel::implConstruct()
258 m_bOriginalNumeric
= false;
260 m_xOriginalFormatter
= nullptr;
261 m_nKeyType
= NumberFormat::UNDEFINED
;
262 m_aNullDate
= DBTypeConversion::getStandardDate();
263 // default our formats supplier
264 osl_atomic_increment(&m_refCount
);
265 setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
266 osl_atomic_decrement(&m_refCount
);
267 startAggregatePropertyListening( PROPERTY_FORMATKEY
);
268 startAggregatePropertyListening( PROPERTY_FORMATSSUPPLIER
);
270 OFormattedModel::OFormattedModel(const Reference
<XComponentContext
>& _rxFactory
)
271 :OEditBaseModel(_rxFactory
, VCL_CONTROLMODEL_FORMATTEDFIELD
, FRM_SUN_CONTROL_FORMATTEDFIELD
, true, true )
272 // use the old control name for compytibility reasons
273 ,OErrorBroadcaster( OComponentHelper::rBHelper
)
276 m_nClassId
= FormComponentType::TEXTFIELD
;
277 initValueProperty( PROPERTY_EFFECTIVE_VALUE
, PROPERTY_ID_EFFECTIVE_VALUE
);
279 OFormattedModel::OFormattedModel( const OFormattedModel
* _pOriginal
, const Reference
< XComponentContext
>& _rxFactory
)
280 :OEditBaseModel( _pOriginal
, _rxFactory
)
281 ,OErrorBroadcaster( OComponentHelper::rBHelper
)
286 OFormattedModel::~OFormattedModel()
291 IMPLEMENT_DEFAULT_CLONING( OFormattedModel
)
293 void SAL_CALL
OFormattedModel::disposing()
295 OErrorBroadcaster::disposing();
296 OEditBaseModel::disposing();
300 css::uno::Sequence
<OUString
> OFormattedModel::getSupportedServiceNames()
302 css::uno::Sequence
<OUString
> aSupported
= OEditBaseModel::getSupportedServiceNames();
303 sal_Int32 nOldLen
= aSupported
.getLength();
304 aSupported
.realloc( nOldLen
+ 9 );
305 OUString
* pStoreTo
= aSupported
.getArray() + nOldLen
;
306 *pStoreTo
++ = BINDABLE_CONTROL_MODEL
;
307 *pStoreTo
++ = DATA_AWARE_CONTROL_MODEL
;
308 *pStoreTo
++ = VALIDATABLE_CONTROL_MODEL
;
309 *pStoreTo
++ = BINDABLE_DATA_AWARE_CONTROL_MODEL
;
310 *pStoreTo
++ = VALIDATABLE_BINDABLE_CONTROL_MODEL
;
311 *pStoreTo
++ = FRM_SUN_COMPONENT_FORMATTEDFIELD
;
312 *pStoreTo
++ = FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD
;
313 *pStoreTo
++ = BINDABLE_DATABASE_FORMATTED_FIELD
;
314 *pStoreTo
++ = FRM_COMPONENT_FORMATTEDFIELD
;
319 Any SAL_CALL
OFormattedModel::queryAggregation(const Type
& _rType
)
321 Any aReturn
= OEditBaseModel::queryAggregation( _rType
);
322 return aReturn
.hasValue() ? aReturn
: OErrorBroadcaster::queryInterface( _rType
);
326 Sequence
< Type
> OFormattedModel::_getTypes()
328 return ::comphelper::concatSequences(
329 OEditBaseModel::_getTypes(),
330 OErrorBroadcaster::getTypes()
335 OUString SAL_CALL
OFormattedModel::getServiceName()
337 return FRM_COMPONENT_EDIT
;
341 void OFormattedModel::describeFixedProperties( Sequence
< Property
>& _rProps
) const
343 BEGIN_DESCRIBE_PROPERTIES( 3, OEditBaseModel
)
344 DECL_BOOL_PROP1(EMPTY_IS_NULL
, BOUND
);
345 DECL_PROP1(TABINDEX
, sal_Int16
, BOUND
);
346 DECL_BOOL_PROP2(FILTERPROPOSAL
, BOUND
, MAYBEDEFAULT
);
347 END_DESCRIBE_PROPERTIES();
350 void OFormattedModel::describeAggregateProperties( Sequence
< Property
>& _rAggregateProps
) const
352 OEditBaseModel::describeAggregateProperties( _rAggregateProps
);
353 // TreatAsNumeric is not transient: we want to attach it to the UI
354 // This is necessary to make EffectiveDefault (which may be text or a number) meaningful
355 ModifyPropertyAttributes(_rAggregateProps
, PROPERTY_TREATASNUMERIC
, 0, PropertyAttribute::TRANSIENT
);
356 // Same for FormatKey
357 // (though the paragraph above for the TreatAsNumeric does not hold anymore - we do not have an UI for this.
358 // But we have for the format key ...)
359 ModifyPropertyAttributes(_rAggregateProps
, PROPERTY_FORMATKEY
, 0, PropertyAttribute::TRANSIENT
);
360 RemoveProperty(_rAggregateProps
, PROPERTY_STRICTFORMAT
);
361 // no strict format property for formatted fields: it does not make sense, 'cause
362 // there is no general way to decide which characters/sub strings are allowed during the input of an
363 // arbitrary formatted control
366 void OFormattedModel::setPropertyToDefaultByHandle(sal_Int32 nHandle
)
368 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
370 Reference
<XNumberFormatsSupplier
> xSupplier
= calcDefaultFormatsSupplier();
371 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::setPropertyToDefaultByHandle(FORMATSSUPPLIER) : have no aggregate !");
372 if (m_xAggregateSet
.is())
373 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(xSupplier
));
376 OEditBaseModel::setPropertyToDefaultByHandle(nHandle
);
379 void OFormattedModel::setPropertyToDefault(const OUString
& aPropertyName
)
381 OPropertyArrayAggregationHelper
& rPH
= m_aPropertyBagHelper
.getInfoHelper();
382 sal_Int32 nHandle
= rPH
.getHandleByName( aPropertyName
);
383 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
384 setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
386 OEditBaseModel::setPropertyToDefault(aPropertyName
);
389 Any
OFormattedModel::getPropertyDefaultByHandle( sal_Int32 nHandle
) const
391 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
393 Reference
<XNumberFormatsSupplier
> xSupplier
= calcDefaultFormatsSupplier();
394 return makeAny(xSupplier
);
397 return OEditBaseModel::getPropertyDefaultByHandle(nHandle
);
400 Any SAL_CALL
OFormattedModel::getPropertyDefault( const OUString
& aPropertyName
)
402 OPropertyArrayAggregationHelper
& rPH
= m_aPropertyBagHelper
.getInfoHelper();
403 sal_Int32 nHandle
= rPH
.getHandleByName( aPropertyName
);
404 if (nHandle
== PROPERTY_ID_FORMATSSUPPLIER
)
405 return getPropertyDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER
);
407 return OEditBaseModel::getPropertyDefault(aPropertyName
);
410 void OFormattedModel::_propertyChanged( const css::beans::PropertyChangeEvent
& evt
)
412 // TODO: check how this works with external bindings
413 OSL_ENSURE( evt
.Source
== m_xAggregateSet
, "OFormattedModel::_propertyChanged: where did this come from?" );
414 if ( evt
.Source
!= m_xAggregateSet
)
417 if ( evt
.PropertyName
== PROPERTY_FORMATKEY
)
419 if ( evt
.NewValue
.getValueType().getTypeClass() == TypeClass_LONG
)
423 ::osl::MutexGuard
aGuard( m_aMutex
);
424 Reference
<XNumberFormatsSupplier
> xSupplier( calcFormatsSupplier() );
425 m_nKeyType
= getNumberFormatType(xSupplier
->getNumberFormats(), getINT32( evt
.NewValue
) );
426 // as m_aSaveValue (which is used by commitControlValueToDbColumn) is format dependent we have
427 // to recalc it, which is done by translateDbColumnToControlValue
428 if ( m_xColumn
.is() && m_xAggregateFastSet
.is() && !m_xCursor
->isBeforeFirst() && !m_xCursor
->isAfterLast())
430 setControlValue( translateDbColumnToControlValue(), eOther
);
432 // if we're connected to an external value binding, then re-calculate the type
433 // used to exchange the value - it depends on the format, too
434 if ( hasExternalValueBinding() )
436 calculateExternalValueType();
439 catch(const Exception
&)
445 if ( evt
.PropertyName
== PROPERTY_FORMATSSUPPLIER
)
447 updateFormatterNullDate();
450 OBoundControlModel::_propertyChanged( evt
);
453 void OFormattedModel::updateFormatterNullDate()
455 // calc the current NULL date
456 Reference
< XNumberFormatsSupplier
> xSupplier( calcFormatsSupplier() );
457 if ( xSupplier
.is() )
458 xSupplier
->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate
;
461 Reference
< XNumberFormatsSupplier
> OFormattedModel::calcFormatsSupplier() const
463 Reference
<XNumberFormatsSupplier
> xSupplier
;
464 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::calcFormatsSupplier : have no aggregate !");
465 // Does my aggregate model have a FormatSupplier?
466 if( m_xAggregateSet
.is() )
467 m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
) >>= xSupplier
;
469 // check if my parent form has a supplier
470 xSupplier
= calcFormFormatsSupplier();
472 xSupplier
= calcDefaultFormatsSupplier();
473 DBG_ASSERT(xSupplier
.is(), "OFormattedModel::calcFormatsSupplier : no supplier !");
474 // We should have one by now
478 Reference
<XNumberFormatsSupplier
> OFormattedModel::calcFormFormatsSupplier() const
480 Reference
<XChild
> xMe(
481 static_cast<XWeak
*>(const_cast<OFormattedModel
*>(this)),
482 css::uno::UNO_QUERY
);
483 // By this we make sure that we get the right object even when aggregating
484 DBG_ASSERT(xMe
.is(), "OFormattedModel::calcFormFormatsSupplier : I should have a content interface !");
485 // Iterate through until we reach a StartForm (starting with an own Parent)
486 Reference
<XChild
> xParent(xMe
->getParent(), UNO_QUERY
);
487 Reference
<XForm
> xNextParentForm(xParent
, UNO_QUERY
);
488 while (!xNextParentForm
.is() && xParent
.is())
490 xParent
.set(xParent
->getParent(), css::uno::UNO_QUERY
);
491 xNextParentForm
.set(xParent
, css::uno::UNO_QUERY
);
493 if (!xNextParentForm
.is())
495 OSL_FAIL("OFormattedModel::calcFormFormatsSupplier : have no ancestor which is a form !");
498 // The FormatSupplier of my ancestor (if it has one)
499 Reference
< XRowSet
> xRowSet( xNextParentForm
, UNO_QUERY
);
500 Reference
< XNumberFormatsSupplier
> xSupplier
;
502 xSupplier
= getNumberFormats( getConnection(xRowSet
), true, getContext() );
506 Reference
< XNumberFormatsSupplier
> OFormattedModel::calcDefaultFormatsSupplier() const
508 return StandardFormatsSupplier::get( getContext() );
512 void OFormattedModel::loaded(const EventObject
& rEvent
)
514 // HACK: our onConnectedDbColumn accesses our NumberFormatter which locks the solar mutex (as it doesn't have
515 // an own one). To prevent deadlocks with other threads which may request a property from us in an
516 // UI-triggered action (e.g. a tooltip) we lock the solar mutex _here_ before our base class locks
517 // its own mutex (which is used for property requests)
518 // alternative a): we use two mutexes, one which is passed to the OPropertysetHelper and used for
519 // property requests and one for our own code. This would need a lot of code rewriting
520 // alternative b): The NumberFormatter has to be really threadsafe (with an own mutex), which is
521 // the only "clean" solution for me.
522 SolarMutexGuard aGuard
;
523 OEditBaseModel::loaded(rEvent
);
526 void OFormattedModel::onConnectedDbColumn( const Reference
< XInterface
>& _rxForm
)
528 m_xOriginalFormatter
= nullptr;
529 // get some properties of the field
530 Reference
<XPropertySet
> xField
= getField();
531 sal_Int32 nFormatKey
= 0;
532 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::onConnectedDbColumn : have no aggregate !");
533 if (m_xAggregateSet
.is())
534 { // all the following doesn't make any sense if we have no aggregate ...
535 Any aSupplier
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
);
536 DBG_ASSERT( aSupplier
.hasValue(), "OFormattedModel::onConnectedDbColumn : invalid property value !" );
537 // This should've been set to the correct value in the ctor or in the read
538 Any aFmtKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
539 if ( !(aFmtKey
>>= nFormatKey
) )
540 { // nobody gave us a format to use. So we examine the field we're bound to for a
541 // format key, and use it ourself, too
542 sal_Int32 nType
= DataType::VARCHAR
;
545 aFmtKey
= xField
->getPropertyValue(PROPERTY_FORMATKEY
);
546 xField
->getPropertyValue(PROPERTY_FIELDTYPE
) >>= nType
;
548 Reference
<XNumberFormatsSupplier
> xSupplier
= calcFormFormatsSupplier();
549 DBG_ASSERT(xSupplier
.is(), "OFormattedModel::onConnectedDbColumn : bound to a field but no parent with a formatter ? how this ?");
552 m_bOriginalNumeric
= getBOOL(getPropertyValue(PROPERTY_TREATASNUMERIC
));
553 if (!aFmtKey
.hasValue())
554 { // we aren't bound to a field (or this field's format is invalid)
555 // -> determine the standard text (or numeric) format of the supplier
556 Reference
<XNumberFormatTypes
> xTypes(xSupplier
->getNumberFormats(), UNO_QUERY
);
559 Locale aApplicationLocale
= Application::GetSettings().GetUILanguageTag().getLocale();
560 if (m_bOriginalNumeric
)
561 aFmtKey
<<= xTypes
->getStandardFormat(NumberFormat::NUMBER
, aApplicationLocale
);
563 aFmtKey
<<= xTypes
->getStandardFormat(NumberFormat::TEXT
, aApplicationLocale
);
566 aSupplier
>>= m_xOriginalFormatter
;
567 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(xSupplier
));
568 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, aFmtKey
);
569 // Adapt the NumericFalg to my bound field
576 case DataType::BOOLEAN
:
577 case DataType::TINYINT
:
578 case DataType::SMALLINT
:
579 case DataType::INTEGER
:
580 case DataType::BIGINT
:
581 case DataType::FLOAT
:
583 case DataType::DOUBLE
:
584 case DataType::NUMERIC
:
585 case DataType::DECIMAL
:
588 case DataType::TIMESTAMP
:
594 m_bNumeric
= m_bOriginalNumeric
;
595 setPropertyValue(PROPERTY_TREATASNUMERIC
, makeAny(m_bNumeric
));
596 OSL_VERIFY( aFmtKey
>>= nFormatKey
);
600 Reference
<XNumberFormatsSupplier
> xSupplier
= calcFormatsSupplier();
601 m_bNumeric
= getBOOL( getPropertyValue( PROPERTY_TREATASNUMERIC
) );
602 m_nKeyType
= getNumberFormatType( xSupplier
->getNumberFormats(), nFormatKey
);
603 xSupplier
->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate
;
604 OEditBaseModel::onConnectedDbColumn( _rxForm
);
607 void OFormattedModel::onDisconnectedDbColumn()
609 OEditBaseModel::onDisconnectedDbColumn();
610 if (m_xOriginalFormatter
.is())
611 { // Our aggregated model does not hold any Format information
612 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(m_xOriginalFormatter
));
613 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, Any());
614 setPropertyValue(PROPERTY_TREATASNUMERIC
, makeAny(m_bOriginalNumeric
));
615 m_xOriginalFormatter
= nullptr;
617 m_nKeyType
= NumberFormat::UNDEFINED
;
618 m_aNullDate
= DBTypeConversion::getStandardDate();
621 void OFormattedModel::write(const Reference
<XObjectOutputStream
>& _rxOutStream
)
623 OEditBaseModel::write(_rxOutStream
);
624 _rxOutStream
->writeShort(0x0003);
625 DBG_ASSERT(m_xAggregateSet
.is(), "OFormattedModel::write : have no aggregate !");
626 // Bring my Format (may be void) to a persistent Format.
627 // The Supplier together with the Key is already persistent, but that doesn't mean
628 // we have to save the Supplier (which would be quite some overhead)
629 Reference
<XNumberFormatsSupplier
> xSupplier
;
631 bool bVoidKey
= true;
632 if (m_xAggregateSet
.is())
634 Any aSupplier
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATSSUPPLIER
);
635 if (aSupplier
.getValueType().getTypeClass() != TypeClass_VOID
)
637 OSL_VERIFY( aSupplier
>>= xSupplier
);
639 aFmtKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
640 bVoidKey
= (!xSupplier
.is() || !aFmtKey
.hasValue()) || (isLoaded() && m_xOriginalFormatter
.is());
641 // (no Format and/or Key) OR (loaded and faked Formatter)
643 _rxOutStream
->writeBoolean(!bVoidKey
);
646 // Create persistent values from the FormatKey and the Formatter
647 Any aKey
= m_xAggregateSet
->getPropertyValue(PROPERTY_FORMATKEY
);
648 sal_Int32 nKey
= aKey
.hasValue() ? getINT32(aKey
) : 0;
649 Reference
<XNumberFormats
> xFormats
= xSupplier
->getNumberFormats();
650 OUString sFormatDescription
;
651 LanguageType eFormatLanguage
= LANGUAGE_DONTKNOW
;
652 static const char s_aLocaleProp
[] = "Locale";
653 Reference
<css::beans::XPropertySet
> xFormat
= xFormats
->getByKey(nKey
);
654 if (hasProperty(s_aLocaleProp
, xFormat
))
656 Any aLocale
= xFormat
->getPropertyValue(s_aLocaleProp
);
657 DBG_ASSERT(aLocale
.has
<Locale
>(), "OFormattedModel::write : invalid language property !");
658 if (auto pLocale
= o3tl::tryAccess
<Locale
>(aLocale
))
660 eFormatLanguage
= LanguageTag::convertToLanguageType( *pLocale
, false);
663 static const char s_aFormatStringProp
[] = "FormatString";
664 if (hasProperty(s_aFormatStringProp
, xFormat
))
665 xFormat
->getPropertyValue(s_aFormatStringProp
) >>= sFormatDescription
;
666 _rxOutStream
->writeUTF(sFormatDescription
);
667 _rxOutStream
->writeLong(static_cast<sal_uInt16
>(eFormatLanguage
));
669 // version 2 : write the properties common to all OEditBaseModels
670 writeCommonEditProperties(_rxOutStream
);
671 // version 3 : write the effective value property of the aggregate
672 // Due to a bug within the UnoControlFormattedFieldModel implementation (our default aggregate)
673 // this props value isn't correctly read and this can't be corrected without being incompatible.
674 // so we have our own handling.
675 // and to be a little bit more compatible we make the following section skippable
677 OStreamSection
aDownCompat(_rxOutStream
);
678 // a sub version within the skippable block
679 _rxOutStream
->writeShort(0x0000);
680 // version 0: the effective value of the aggregate
682 if (m_xAggregateSet
.is())
684 try { aEffectiveValue
= m_xAggregateSet
->getPropertyValue(PROPERTY_EFFECTIVE_VALUE
); } catch(const Exception
&) { }
687 OStreamSection
aDownCompat2(_rxOutStream
);
688 switch (aEffectiveValue
.getValueType().getTypeClass())
690 case TypeClass_STRING
:
691 _rxOutStream
->writeShort(0x0000);
692 _rxOutStream
->writeUTF(::comphelper::getString(aEffectiveValue
));
694 case TypeClass_DOUBLE
:
695 _rxOutStream
->writeShort(0x0001);
696 _rxOutStream
->writeDouble(::comphelper::getDouble(aEffectiveValue
));
698 default: // void and all unknown states
699 DBG_ASSERT(!aEffectiveValue
.hasValue(), "FmXFormattedModel::write : unknown property value type !");
700 _rxOutStream
->writeShort(0x0002);
707 void OFormattedModel::read(const Reference
<XObjectInputStream
>& _rxInStream
)
709 OEditBaseModel::read(_rxInStream
);
710 sal_uInt16 nVersion
= _rxInStream
->readShort();
711 Reference
<XNumberFormatsSupplier
> xSupplier
;
719 bool bNonVoidKey
= _rxInStream
->readBoolean();
722 // read string and language...
723 OUString sFormatDescription
= _rxInStream
->readUTF();
724 LanguageType
eDescriptionLanguage(_rxInStream
->readLong());
725 // and let a formatter roll dice based on that to create a key...
726 xSupplier
= calcFormatsSupplier();
727 // calcFormatsSupplier first takes the one from the model, then one from the starform, then a new one...
728 Reference
<XNumberFormats
> xFormats
= xSupplier
->getNumberFormats();
731 Locale
aDescriptionLanguage( LanguageTag::convertToLocale(eDescriptionLanguage
));
732 nKey
= xFormats
->queryKey(sFormatDescription
, aDescriptionLanguage
, false);
733 if (nKey
== sal_Int32(-1))
734 { // does not yet exist in my formatter...
735 nKey
= xFormats
->addNew(sFormatDescription
, aDescriptionLanguage
);
739 if ((nVersion
== 0x0002) || (nVersion
== 0x0003))
740 readCommonEditProperties(_rxInStream
);
741 if (nVersion
== 0x0003)
742 { // since version 3 there is a "skippable" block at this position
743 OStreamSection
aDownCompat(_rxInStream
);
744 _rxInStream
->readShort(); // sub-version
745 // version 0 and higher: the "effective value" property
748 OStreamSection
aDownCompat2(_rxInStream
);
749 switch (_rxInStream
->readShort())
752 aEffectiveValue
<<= _rxInStream
->readUTF();
755 aEffectiveValue
<<= _rxInStream
->readDouble();
760 OSL_FAIL("FmXFormattedModel::read : unknown effective value type!");
763 // this property is only to be set if we have no control source: in all other cases the base class made a
764 // reset after it's read and this set the effective value to a default value
765 if ( m_xAggregateSet
.is() && getControlSource().isEmpty() )
769 m_xAggregateSet
->setPropertyValue(PROPERTY_EFFECTIVE_VALUE
, aEffectiveValue
);
771 catch(const Exception
&)
779 OSL_FAIL("OFormattedModel::read : unknown version !");
780 // then the format of the aggregated set stay like it was during creation: void
781 defaultCommonEditProperties();
784 if ((nKey
!= -1) && m_xAggregateSet
.is())
786 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATSSUPPLIER
, makeAny(xSupplier
));
787 m_xAggregateSet
->setPropertyValue(PROPERTY_FORMATKEY
, makeAny(nKey
));
791 setPropertyToDefault(PROPERTY_FORMATSSUPPLIER
);
792 setPropertyToDefault(PROPERTY_FORMATKEY
);
796 sal_uInt16
OFormattedModel::getPersistenceFlags() const
798 return (OEditBaseModel::getPersistenceFlags() & ~PF_HANDLE_COMMON_PROPS
);
799 // a) we do our own call to writeCommonEditProperties
802 bool OFormattedModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
804 Any
aControlValue( m_xAggregateFastSet
->getFastPropertyValue( getValuePropertyAggHandle() ) );
805 if ( aControlValue
!= m_aSaveValue
)
807 // empty string + EmptyIsNull = void
808 if ( !aControlValue
.hasValue()
809 || ( ( aControlValue
.getValueType().getTypeClass() == TypeClass_STRING
)
810 && getString( aControlValue
).isEmpty()
814 m_xColumnUpdate
->updateNull();
820 if ( aControlValue
.getValueType().getTypeClass() == TypeClass_DOUBLE
|| (aControlValue
>>= f
)) // #i110323
822 DBTypeConversion::setValue( m_xColumnUpdate
, m_aNullDate
, getDouble( aControlValue
), m_nKeyType
);
826 DBG_ASSERT( aControlValue
.getValueType().getTypeClass() == TypeClass_STRING
, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" );
827 m_xColumnUpdate
->updateString( getString( aControlValue
) );
830 catch(const Exception
&)
835 m_aSaveValue
= aControlValue
;
840 void OFormattedModel::onConnectedExternalValue( )
842 OEditBaseModel::onConnectedExternalValue();
843 updateFormatterNullDate();
846 Any
OFormattedModel::translateExternalValueToControlValue( const Any
& _rExternalValue
) const
849 switch( _rExternalValue
.getValueTypeClass() )
853 case TypeClass_STRING
:
854 aControlValue
= _rExternalValue
;
856 case TypeClass_BOOLEAN
:
858 bool bExternalValue
= false;
859 _rExternalValue
>>= bExternalValue
;
860 aControlValue
<<= static_cast<double>( bExternalValue
? 1 : 0 );
865 if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::Date
>::get() ) )
867 css::util::Date aDate
;
868 _rExternalValue
>>= aDate
;
869 aControlValue
<<= DBTypeConversion::toDouble( aDate
, m_aNullDate
);
871 else if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::Time
>::get() ) )
873 css::util::Time aTime
;
874 _rExternalValue
>>= aTime
;
875 aControlValue
<<= DBTypeConversion::toDouble( aTime
);
877 else if ( _rExternalValue
.getValueType().equals( cppu::UnoType
< css::util::DateTime
>::get() ) )
879 css::util::DateTime aDateTime
;
880 _rExternalValue
>>= aDateTime
;
881 aControlValue
<<= DBTypeConversion::toDouble( aDateTime
, m_aNullDate
);
885 OSL_ENSURE( _rExternalValue
.getValueTypeClass() == TypeClass_DOUBLE
,
886 "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" );
888 OSL_VERIFY( _rExternalValue
>>= fValue
);
889 aControlValue
<<= fValue
;
893 return aControlValue
;
896 Any
OFormattedModel::translateControlValueToExternalValue( ) const
898 OSL_PRECOND( hasExternalValueBinding(),
899 "OFormattedModel::translateControlValueToExternalValue: precondition not met!" );
900 Any
aControlValue( getControlValue() );
901 if ( !aControlValue
.hasValue() )
902 return aControlValue
;
904 // translate into the external value type
905 Type
aExternalValueType( getExternalValueType() );
906 switch ( aExternalValueType
.getTypeClass() )
908 case TypeClass_STRING
:
911 if ( aControlValue
>>= sString
)
913 aExternalValue
<<= sString
;
918 case TypeClass_BOOLEAN
:
921 OSL_VERIFY( aControlValue
>>= fValue
);
922 // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
923 // and the control value is a string. This implies some weird misconfiguration
924 // of the FormattedModel, so we won't care for it for the moment.
925 aExternalValue
<<= fValue
!= 0.0;
931 OSL_VERIFY( aControlValue
>>= fValue
);
932 // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
933 // and the control value is a string. This implies some weird misconfiguration
934 // of the FormattedModel, so we won't care for it for the moment.
935 if ( aExternalValueType
.equals( cppu::UnoType
< css::util::Date
>::get() ) )
937 aExternalValue
<<= DBTypeConversion::toDate( fValue
, m_aNullDate
);
939 else if ( aExternalValueType
.equals( cppu::UnoType
< css::util::Time
>::get() ) )
941 aExternalValue
<<= DBTypeConversion::toTime( fValue
);
943 else if ( aExternalValueType
.equals( cppu::UnoType
< css::util::DateTime
>::get() ) )
945 aExternalValue
<<= DBTypeConversion::toDateTime( fValue
, m_aNullDate
);
949 OSL_ENSURE( aExternalValueType
.equals( cppu::UnoType
< double >::get() ),
950 "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" );
951 aExternalValue
<<= fValue
;
956 return aExternalValue
;
959 Any
OFormattedModel::translateDbColumnToControlValue()
962 m_aSaveValue
<<= DBTypeConversion::getValue( m_xColumn
, m_aNullDate
); // #100056# OJ
964 m_aSaveValue
<<= m_xColumn
->getString();
965 if ( m_xColumn
->wasNull() )
966 m_aSaveValue
.clear();
970 Sequence
< Type
> OFormattedModel::getSupportedBindingTypes()
972 ::std::vector
< Type
> aTypes
;
973 switch ( m_nKeyType
& ~NumberFormat::DEFINED
)
975 case NumberFormat::DATE
:
976 aTypes
.push_back(cppu::UnoType
< css::util::Date
>::get() );
978 case NumberFormat::TIME
:
979 aTypes
.push_back(cppu::UnoType
< css::util::Time
>::get() );
981 case NumberFormat::DATETIME
:
982 aTypes
.push_back(cppu::UnoType
< css::util::DateTime
>::get() );
984 case NumberFormat::TEXT
:
985 aTypes
.push_back(cppu::UnoType
< OUString
>::get() );
987 case NumberFormat::LOGICAL
:
988 aTypes
.push_back(cppu::UnoType
< sal_Bool
>::get() );
991 aTypes
.push_back( cppu::UnoType
< double >::get() );
992 return comphelper::containerToSequence(aTypes
);
995 Any
OFormattedModel::getDefaultForReset() const
997 return m_xAggregateSet
->getPropertyValue( PROPERTY_EFFECTIVE_DEFAULT
);
1000 void OFormattedModel::resetNoBroadcast()
1002 OEditBaseModel::resetNoBroadcast();
1003 m_aSaveValue
.clear();
1008 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1009 com_sun_star_form_OFormattedControl_get_implementation(css::uno::XComponentContext
* component
,
1010 css::uno::Sequence
<css::uno::Any
> const &)
1012 return cppu::acquire(new frm::OFormattedControl(component
));
1015 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */