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