nss: upgrade to release 3.73
[LibreOffice.git] / forms / source / component / FormattedField.cxx
blob18cb21882d6a8fcab7b17a374727960674a5544e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
53 #include <vector>
54 #include <algorithm>
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;
71 namespace frm
73 namespace {
75 class StandardFormatsSupplier : protected SvNumberFormatsSupplierObj, public ::utl::ITerminationListener
77 protected:
78 std::unique_ptr<SvNumberFormatter> m_pMyPrivateFormatter;
79 static WeakReference< XNumberFormatsSupplier > s_xDefaultFormatsSupplier;
80 public:
81 static Reference< XNumberFormatsSupplier > get( const Reference< XComponentContext >& _rxORB );
82 protected:
83 StandardFormatsSupplier(const Reference< XComponentContext >& _rxFactory,LanguageType _eSysLanguage);
84 virtual ~StandardFormatsSupplier() override;
85 protected:
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());
98 // #i29147#
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() )
112 return xSupplier;
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
123 // the supplier
124 return xSupplier;
125 s_xDefaultFormatsSupplier = xNewlyCreatedSupplier;
127 return xNewlyCreatedSupplier;
129 bool StandardFormatsSupplier::queryTermination() const
131 return true;
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
138 // #i29147#
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);
155 return aReturn;
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()
173 if( m_nKeyEvent )
174 Application::RemoveUserEvent( m_nKeyEvent );
175 if (!OComponentHelper::rBHelper.bDisposed)
177 acquire();
178 dispose();
182 // XKeyListener
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 )
190 return;
191 // Is the control located in a form with a Submit URL?
192 Reference<css::beans::XPropertySet> xSet(getModel(), UNO_QUERY);
193 if( !xSet.is() )
194 return;
195 Reference<XFormComponent> xFComp(xSet, UNO_QUERY);
196 css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
197 if( !xParent.is() )
198 return;
199 Reference<css::beans::XPropertySet> xFormSet(xParent, UNO_QUERY);
200 if( !xFormSet.is() )
201 return;
202 Any aTmp(xFormSet->getPropertyValue( PROPERTY_TARGET_URL ));
203 if (!aTmp.has<OUString>() ||
204 getString(aTmp).isEmpty() )
205 return;
206 Reference<XIndexAccess> xElements(xParent, UNO_QUERY);
207 sal_Int32 nCount = xElements->getCount();
208 if( nCount > 1 )
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
219 if (xFCSet != xSet)
220 return;
224 // Because we're still in the Handler, execute submit asynchronously
225 if( m_nKeyEvent )
226 Application::RemoveUserEvent( m_nKeyEvent );
227 m_nKeyEvent = Application::PostUserEvent( LINK(this, OFormattedControl,
228 OnKeyPressed) );
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);
241 if (xSubmit.is())
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;
252 return aSupported;
255 void OFormattedModel::implConstruct()
257 // members
258 m_bOriginalNumeric = false;
259 m_bNumeric = 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 )
275 implConstruct();
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 )
283 implConstruct();
286 OFormattedModel::~OFormattedModel()
290 // XCloneable
291 IMPLEMENT_DEFAULT_CLONING( OFormattedModel )
293 void SAL_CALL OFormattedModel::disposing()
295 OErrorBroadcaster::disposing();
296 OEditBaseModel::disposing();
299 // XServiceInfo
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;
315 return aSupported;
318 // XAggregation
319 Any SAL_CALL OFormattedModel::queryAggregation(const Type& _rType)
321 Any aReturn = OEditBaseModel::queryAggregation( _rType );
322 return aReturn.hasValue() ? aReturn : OErrorBroadcaster::queryInterface( _rType );
325 // XTypeProvider
326 Sequence< Type > OFormattedModel::_getTypes()
328 return ::comphelper::concatSequences(
329 OEditBaseModel::_getTypes(),
330 OErrorBroadcaster::getTypes()
334 // XPersistObject
335 OUString SAL_CALL OFormattedModel::getServiceName()
337 return FRM_COMPONENT_EDIT;
340 // XPropertySet
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));
375 else
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);
385 else
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);
396 else
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);
406 else
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 )
415 return;
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&)
443 return;
445 if ( evt.PropertyName == PROPERTY_FORMATSSUPPLIER )
447 updateFormatterNullDate();
448 return;
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;
468 if (!xSupplier.is())
469 // check if my parent form has a supplier
470 xSupplier = calcFormFormatsSupplier();
471 if (!xSupplier.is())
472 xSupplier = calcDefaultFormatsSupplier();
473 DBG_ASSERT(xSupplier.is(), "OFormattedModel::calcFormatsSupplier : no supplier !");
474 // We should have one by now
475 return xSupplier;
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 !");
496 return nullptr;
498 // The FormatSupplier of my ancestor (if it has one)
499 Reference< XRowSet > xRowSet( xNextParentForm, UNO_QUERY );
500 Reference< XNumberFormatsSupplier > xSupplier;
501 if (xRowSet.is())
502 xSupplier = getNumberFormats( getConnection(xRowSet), true, getContext() );
503 return xSupplier;
506 Reference< XNumberFormatsSupplier > OFormattedModel::calcDefaultFormatsSupplier() const
508 return StandardFormatsSupplier::get( getContext() );
511 // XBoundComponent
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;
543 if (xField.is())
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 ?");
550 if (xSupplier.is())
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);
557 if (xTypes.is())
559 Locale aApplicationLocale = Application::GetSettings().GetUILanguageTag().getLocale();
560 if (m_bOriginalNumeric)
561 aFmtKey <<= xTypes->getStandardFormat(NumberFormat::NUMBER, aApplicationLocale);
562 else
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
570 if (xField.is())
572 m_bNumeric = false;
573 switch (nType)
575 case DataType::BIT:
576 case DataType::BOOLEAN:
577 case DataType::TINYINT:
578 case DataType::SMALLINT:
579 case DataType::INTEGER:
580 case DataType::BIGINT:
581 case DataType::FLOAT:
582 case DataType::REAL:
583 case DataType::DOUBLE:
584 case DataType::NUMERIC:
585 case DataType::DECIMAL:
586 case DataType::DATE:
587 case DataType::TIME:
588 case DataType::TIMESTAMP:
589 m_bNumeric = true;
590 break;
593 else
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;
630 Any aFmtKey;
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);
644 if (!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
681 Any aEffectiveValue;
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));
693 break;
694 case TypeClass_DOUBLE:
695 _rxOutStream->writeShort(0x0001);
696 _rxOutStream->writeDouble(::comphelper::getDouble(aEffectiveValue));
697 break;
698 default: // void and all unknown states
699 DBG_ASSERT(!aEffectiveValue.hasValue(), "FmXFormattedModel::write : unknown property value type !");
700 _rxOutStream->writeShort(0x0002);
701 break;
707 void OFormattedModel::read(const Reference<XObjectInputStream>& _rxInStream)
709 OEditBaseModel::read(_rxInStream);
710 sal_uInt16 nVersion = _rxInStream->readShort();
711 Reference<XNumberFormatsSupplier> xSupplier;
712 sal_Int32 nKey = -1;
713 switch (nVersion)
715 case 0x0001 :
716 case 0x0002 :
717 case 0x0003 :
719 bool bNonVoidKey = _rxInStream->readBoolean();
720 if (bNonVoidKey)
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();
729 if (xFormats.is())
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
746 Any aEffectiveValue;
748 OStreamSection aDownCompat2(_rxInStream);
749 switch (_rxInStream->readShort())
751 case 0: // String
752 aEffectiveValue <<= _rxInStream->readUTF();
753 break;
754 case 1: // double
755 aEffectiveValue <<= _rxInStream->readDouble();
756 break;
757 case 2:
758 break;
759 case 3:
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&)
777 break;
778 default :
779 OSL_FAIL("OFormattedModel::read : unknown version !");
780 // then the format of the aggregated set stay like it was during creation: void
781 defaultCommonEditProperties();
782 break;
784 if ((nKey != -1) && m_xAggregateSet.is())
786 m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, makeAny(xSupplier));
787 m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, makeAny(nKey));
789 else
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()
811 && m_bEmptyIsNull
814 m_xColumnUpdate->updateNull();
815 else
819 double f = 0.0;
820 if ( aControlValue.getValueType().getTypeClass() == TypeClass_DOUBLE || (aControlValue >>= f)) // #i110323
822 DBTypeConversion::setValue( m_xColumnUpdate, m_aNullDate, getDouble( aControlValue ), m_nKeyType );
824 else
826 DBG_ASSERT( aControlValue.getValueType().getTypeClass() == TypeClass_STRING, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" );
827 m_xColumnUpdate->updateString( getString( aControlValue ) );
830 catch(const Exception&)
832 return false;
835 m_aSaveValue = aControlValue;
837 return true;
840 void OFormattedModel::onConnectedExternalValue( )
842 OEditBaseModel::onConnectedExternalValue();
843 updateFormatterNullDate();
846 Any OFormattedModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
848 Any aControlValue;
849 switch( _rExternalValue.getValueTypeClass() )
851 case TypeClass_VOID:
852 break;
853 case TypeClass_STRING:
854 aControlValue = _rExternalValue;
855 break;
856 case TypeClass_BOOLEAN:
858 bool bExternalValue = false;
859 _rExternalValue >>= bExternalValue;
860 aControlValue <<= static_cast<double>( bExternalValue ? 1 : 0 );
862 break;
863 default:
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 );
883 else
885 OSL_ENSURE( _rExternalValue.getValueTypeClass() == TypeClass_DOUBLE,
886 "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" );
887 double fValue = 0;
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;
903 Any aExternalValue;
904 // translate into the external value type
905 Type aExternalValueType( getExternalValueType() );
906 switch ( aExternalValueType.getTypeClass() )
908 case TypeClass_STRING:
910 OUString sString;
911 if ( aControlValue >>= sString )
913 aExternalValue <<= sString;
914 break;
916 [[fallthrough]];
918 case TypeClass_BOOLEAN:
920 double fValue = 0;
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;
927 break;
928 default:
930 double fValue = 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 );
947 else
949 OSL_ENSURE( aExternalValueType.equals( cppu::UnoType< double >::get() ),
950 "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" );
951 aExternalValue <<= fValue;
954 break;
956 return aExternalValue;
959 Any OFormattedModel::translateDbColumnToControlValue()
961 if ( m_bNumeric )
962 m_aSaveValue <<= DBTypeConversion::getValue( m_xColumn, m_aNullDate ); // #100056# OJ
963 else
964 m_aSaveValue <<= m_xColumn->getString();
965 if ( m_xColumn->wasNull() )
966 m_aSaveValue.clear();
967 return m_aSaveValue;
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() );
977 break;
978 case NumberFormat::TIME:
979 aTypes.push_back(cppu::UnoType< css::util::Time >::get() );
980 break;
981 case NumberFormat::DATETIME:
982 aTypes.push_back(cppu::UnoType< css::util::DateTime >::get() );
983 break;
984 case NumberFormat::TEXT:
985 aTypes.push_back(cppu::UnoType< OUString >::get() );
986 break;
987 case NumberFormat::LOGICAL:
988 aTypes.push_back(cppu::UnoType< sal_Bool >::get() );
989 break;
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: */