tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / unoobj / cellvaluebinding.cxx
blob21844d26836596fdd6d9d299de94f61ac329244e
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 .
20 #include "cellvaluebinding.hxx"
21 #include <rtl/math.hxx>
22 #include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
23 #include <com/sun/star/lang/NotInitializedException.hpp>
24 #include <com/sun/star/text/XTextRange.hpp>
25 #include <com/sun/star/table/XCellRange.hpp>
26 #include <com/sun/star/sheet/FormulaResult.hpp>
27 #include <com/sun/star/sheet/XCellAddressable.hpp>
28 #include <com/sun/star/sheet/XCellRangeData.hpp>
29 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
30 #include <com/sun/star/container/XIndexAccess.hpp>
31 #include <com/sun/star/beans/PropertyAttribute.hpp>
32 #include <com/sun/star/beans/NamedValue.hpp>
33 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
34 #include <com/sun/star/util/XNumberFormatTypes.hpp>
35 #include <com/sun/star/util/NumberFormat.hpp>
36 #include <cppuhelper/supportsservice.hxx>
37 #include <comphelper/types.hxx>
38 #include <comphelper/diagnose_ex.hxx>
40 namespace calc
43 #define PROP_HANDLE_BOUND_CELL 1
45 namespace lang = css::lang;
46 using namespace ::com::sun::star::uno;
47 using namespace ::com::sun::star::lang;
48 using namespace ::com::sun::star::table;
49 using namespace ::com::sun::star::text;
50 using namespace ::com::sun::star::sheet;
51 using namespace ::com::sun::star::container;
52 using namespace ::com::sun::star::beans;
53 using namespace ::com::sun::star::util;
54 using namespace ::com::sun::star::form::binding;
56 OCellValueBinding::OCellValueBinding( const Reference< XSpreadsheetDocument >& _rxDocument, bool _bListPos )
57 :m_xDocument( _rxDocument )
58 ,m_bInitialized( false )
59 ,m_bListPos( _bListPos )
61 // register our property at the base class
62 registerPropertyNoMember(
63 u"BoundCell"_ustr,
64 PROP_HANDLE_BOUND_CELL,
65 PropertyAttribute::BOUND | PropertyAttribute::READONLY,
66 cppu::UnoType<CellAddress>::get(),
67 css::uno::Any(CellAddress())
70 // TODO: implement a ReadOnly property as required by the service,
71 // which probably maps to the cell being locked
74 OCellValueBinding::~OCellValueBinding( )
76 if ( !m_bDisposed )
78 acquire(); // prevent duplicate dtor
79 dispose();
83 IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
85 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
87 void OCellValueBinding::disposing( std::unique_lock<std::mutex>& rGuard )
89 Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
90 if ( xBroadcaster.is() )
92 xBroadcaster->removeModifyListener( this );
95 WeakComponentImplHelperBase::disposing(rGuard);
97 // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
98 // for the cell)
101 Reference< XPropertySetInfo > SAL_CALL OCellValueBinding::getPropertySetInfo( )
103 return createPropertySetInfo( getInfoHelper() ) ;
106 ::cppu::IPropertyArrayHelper& OCellValueBinding::getInfoHelper()
108 return *OCellValueBinding_PABase::getArrayHelper();
111 ::cppu::IPropertyArrayHelper* OCellValueBinding::createArrayHelper( ) const
113 Sequence< Property > aProps;
114 describeProperties( aProps );
115 return new ::cppu::OPropertyArrayHelper(aProps);
118 void OCellValueBinding::getFastPropertyValue( std::unique_lock<std::mutex>& /*rGuard*/, Any& _rValue, sal_Int32 _nHandle ) const
120 OSL_ENSURE( _nHandle == PROP_HANDLE_BOUND_CELL, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
121 // we only have this one property...
123 _rValue.clear();
124 Reference< XCellAddressable > xCellAddress( m_xCell, UNO_QUERY );
125 if ( xCellAddress.is() )
126 _rValue <<= xCellAddress->getCellAddress( );
129 Sequence< Type > SAL_CALL OCellValueBinding::getSupportedValueTypes( )
131 std::unique_lock<std::mutex> aGuard(m_aMutex);
132 throwIfDisposed(aGuard);
133 checkInitialized( );
134 return getSupportedValueTypes(aGuard);
137 Sequence< Type > OCellValueBinding::getSupportedValueTypes( std::unique_lock<std::mutex>& /*rGuard*/ ) const
139 sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
140 if ( m_bListPos )
141 ++nCount;
143 Sequence< Type > aTypes( nCount );
144 if ( m_xCell.is() )
146 auto pTypes = aTypes.getArray();
148 // an XCell can be used to set/get "double" values
149 pTypes[0] = ::cppu::UnoType<double>::get();
150 if ( m_xCellText.is() )
152 // an XTextRange can be used to set/get "string" values
153 pTypes[1] = ::cppu::UnoType<OUString>::get();
154 // and additionally, we use it to handle booleans
155 pTypes[2] = ::cppu::UnoType<sal_Bool>::get();
158 // add sal_Int32 only if constructed as ListPositionCellBinding
159 if ( m_bListPos )
160 pTypes[nCount-1] = cppu::UnoType<sal_Int32>::get();
163 return aTypes;
166 sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType )
168 std::unique_lock<std::mutex> aGuard(m_aMutex);
169 throwIfDisposed(aGuard);
170 checkInitialized( );
171 return supportsType(aGuard, aType);
174 bool OCellValueBinding::supportsType( std::unique_lock<std::mutex>& rGuard, const Type& aType ) const
176 // look up in our sequence
177 const Sequence< Type > aSupportedTypes( getSupportedValueTypes(rGuard) );
178 for ( auto const & i : aSupportedTypes )
179 if ( aType == i )
180 return true;
182 return false;
185 Any SAL_CALL OCellValueBinding::getValue( const Type& aType )
187 std::unique_lock<std::mutex> aGuard(m_aMutex);
188 throwIfDisposed(aGuard);
189 checkInitialized( );
190 checkValueType( aGuard, aType );
192 Any aReturn;
193 switch ( aType.getTypeClass() )
195 case TypeClass_STRING:
196 OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::getValue: don't have a text!" );
197 if ( m_xCellText.is() )
198 aReturn <<= m_xCellText->getString();
199 else
200 aReturn <<= OUString();
201 break;
203 case TypeClass_BOOLEAN:
204 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
205 if ( m_xCell.is() )
207 // check if the cell has a numeric value (this might go into a helper function):
209 bool bHasValue = false;
210 CellContentType eCellType = m_xCell->getType();
211 if ( eCellType == CellContentType_VALUE )
212 bHasValue = true;
213 else if ( eCellType == CellContentType_FORMULA )
215 // check if the formula result is a value
216 if ( m_xCell->getError() == 0 )
218 Reference<XPropertySet> xProp( m_xCell, UNO_QUERY );
219 if ( xProp.is() )
221 sal_Int32 nResultType;
222 if ( (xProp->getPropertyValue(u"FormulaResultType2"_ustr) >>= nResultType)
223 && nResultType == FormulaResult::VALUE )
224 bHasValue = true;
229 if ( bHasValue )
231 // 0 is "unchecked", any other value is "checked", regardless of number format
232 double nCellValue = m_xCell->getValue();
233 bool bBoolValue = ( nCellValue != 0.0 );
234 aReturn <<= bBoolValue;
236 // empty cells, text cells and text or error formula results: leave return value empty
238 break;
240 case TypeClass_DOUBLE:
241 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
242 if ( m_xCell.is() )
243 aReturn <<= m_xCell->getValue();
244 else
245 aReturn <<= double(0);
246 break;
248 case TypeClass_LONG:
249 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
250 if ( m_xCell.is() )
252 // The list position value in the cell is 1-based.
253 // We subtract 1 from any cell value (no special handling for 0 or negative values).
255 sal_Int32 nValue = static_cast<sal_Int32>(rtl::math::approxFloor( m_xCell->getValue() ));
256 --nValue;
258 aReturn <<= nValue;
260 else
261 aReturn <<= sal_Int32(0);
262 break;
264 default:
265 OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
266 // a type other than double and string should never have survived the checkValueType
267 // above
269 return aReturn;
272 void SAL_CALL OCellValueBinding::setValue( const Any& aValue )
274 std::unique_lock<std::mutex> aGuard(m_aMutex);
275 throwIfDisposed(aGuard);
276 checkInitialized( );
277 if ( aValue.hasValue() )
278 checkValueType( aGuard, aValue.getValueType() );
280 switch ( aValue.getValueTypeClass() )
282 case TypeClass_STRING:
284 OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::setValue: don't have a text!" );
286 OUString sText;
287 aValue >>= sText;
288 if ( m_xCellText.is() )
290 // might call back into us via modified(EventObject&)
291 aGuard.unlock();
292 m_xCellText->setString( sText );
293 aGuard.lock();
296 break;
298 case TypeClass_BOOLEAN:
300 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
302 // boolean is stored as values 0 or 1
303 // TODO: set the number format to boolean if no format is set?
305 bool bValue( false );
306 aValue >>= bValue;
307 double nCellValue = bValue ? 1.0 : 0.0;
309 if ( m_xCell.is() )
311 // might call back into us via modified(EventObject&)
312 aGuard.unlock();
313 m_xCell->setValue( nCellValue );
314 aGuard.lock();
317 setBooleanFormat();
319 break;
321 case TypeClass_DOUBLE:
323 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
325 double nValue = 0;
326 aValue >>= nValue;
327 if ( m_xCell.is() )
329 // might call back into us via modified(EventObject&)
330 aGuard.unlock();
331 m_xCell->setValue( nValue );
332 aGuard.lock();
335 break;
337 case TypeClass_LONG:
339 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
341 sal_Int32 nValue = 0;
342 aValue >>= nValue; // list index from control layer (0-based)
343 ++nValue; // the list position value in the cell is 1-based
344 if ( m_xCell.is() )
346 // might call back into us via modified(EventObject&)
347 aGuard.unlock();
348 m_xCell->setValue( nValue );
349 aGuard.lock();
352 break;
354 case TypeClass_VOID:
356 // #N/A error value can only be set using XCellRangeData
358 Reference<XCellRangeData> xData( m_xCell, UNO_QUERY );
359 OSL_ENSURE( xData.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
360 if ( xData.is() )
362 Sequence<Any> aInner(1); // one empty element
363 Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
364 // might call back into us via modified(EventObject&)
365 aGuard.unlock();
366 xData->setDataArray( aOuter );
367 aGuard.lock();
370 break;
372 default:
373 OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
374 // a type other than double and string should never have survived the checkValueType
375 // above
379 void OCellValueBinding::setBooleanFormat()
381 // set boolean number format if not already set
383 OUString sPropName( u"NumberFormat"_ustr );
384 Reference<XPropertySet> xCellProp( m_xCell, UNO_QUERY );
385 Reference<XNumberFormatsSupplier> xSupplier( m_xDocument, UNO_QUERY );
386 if ( !(xSupplier.is() && xCellProp.is()) )
387 return;
389 Reference<XNumberFormats> xFormats(xSupplier->getNumberFormats());
390 Reference<XNumberFormatTypes> xTypes( xFormats, UNO_QUERY );
391 if ( !xTypes.is() )
392 return;
394 lang::Locale aLocale;
395 bool bWasBoolean = false;
397 sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) );
398 Reference<XPropertySet> xOldFormat;
401 xOldFormat.set(xFormats->getByKey( nOldIndex ));
403 catch ( Exception& )
405 // non-existing format - can happen, use defaults
407 if ( xOldFormat.is() )
409 // use the locale of the existing format
410 xOldFormat->getPropertyValue(u"Locale"_ustr) >>= aLocale;
412 sal_Int16 nOldType = ::comphelper::getINT16(
413 xOldFormat->getPropertyValue(u"Type"_ustr) );
414 if ( nOldType & NumberFormat::LOGICAL )
415 bWasBoolean = true;
418 if ( !bWasBoolean )
420 sal_Int32 nNewIndex = xTypes->getStandardFormat( NumberFormat::LOGICAL, aLocale );
421 xCellProp->setPropertyValue( sPropName, Any( nNewIndex ) );
425 void OCellValueBinding::checkInitialized()
427 if ( !m_bInitialized )
428 throw NotInitializedException(u"CellValueBinding is not initialized"_ustr, getXWeak());
431 void OCellValueBinding::checkValueType( std::unique_lock<std::mutex>& rGuard, const Type& _rType ) const
433 if ( !supportsType( rGuard, _rType ) )
435 OUString sMessage = "The given type (" +
436 _rType.getTypeName() +
437 ") is not supported by this binding.";
438 // TODO: localize this error message
440 throw IncompatibleTypesException( sMessage, const_cast<OCellValueBinding&>(*this) );
441 // TODO: alternatively use a type converter service for this?
445 OUString SAL_CALL OCellValueBinding::getImplementationName( )
447 return u"com.sun.star.comp.sheet.OCellValueBinding"_ustr;
450 sal_Bool SAL_CALL OCellValueBinding::supportsService( const OUString& _rServiceName )
452 return cppu::supportsService(this, _rServiceName);
455 Sequence< OUString > SAL_CALL OCellValueBinding::getSupportedServiceNames( )
457 Sequence< OUString > aServices( m_bListPos ? 3 : 2 );
458 auto pServices = aServices.getArray();
459 pServices[ 0 ] = "com.sun.star.table.CellValueBinding";
460 pServices[ 1 ] = "com.sun.star.form.binding.ValueBinding";
461 if ( m_bListPos )
462 pServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
463 return aServices;
466 void SAL_CALL OCellValueBinding::addModifyListener( const Reference< XModifyListener >& _rxListener )
468 if ( _rxListener.is() )
470 std::unique_lock<std::mutex> aGuard(m_aMutex);
471 m_aModifyListeners.addInterface( aGuard, _rxListener );
475 void SAL_CALL OCellValueBinding::removeModifyListener( const Reference< XModifyListener >& _rxListener )
477 if ( _rxListener.is() )
479 std::unique_lock<std::mutex> aGuard(m_aMutex);
480 m_aModifyListeners.removeInterface( aGuard, _rxListener );
484 void OCellValueBinding::notifyModified()
486 EventObject aEvent;
487 aEvent.Source.set(*this);
489 std::unique_lock<std::mutex> aGuard(m_aMutex);
490 m_aModifyListeners.forEach(aGuard,
491 [&aEvent] (const css::uno::Reference<css::util::XModifyListener> & l)
495 l->modified( aEvent );
497 catch( const RuntimeException& )
499 // silent this
501 catch( const Exception& )
503 TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
508 void SAL_CALL OCellValueBinding::modified( const EventObject& /* aEvent */ )
510 notifyModified();
513 void SAL_CALL OCellValueBinding::disposing( const EventObject& aEvent )
515 Reference<XInterface> xCellInt( m_xCell, UNO_QUERY );
516 if ( xCellInt == aEvent.Source )
518 // release references to cell object
519 m_xCell.clear();
520 m_xCellText.clear();
524 void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments )
526 if ( m_bInitialized )
527 throw RuntimeException(u"CellValueBinding is already initialized"_ustr, getXWeak());
529 // get the cell address
530 CellAddress aAddress;
531 bool bFoundAddress = false;
533 for ( const Any& rArg : _rArguments )
535 NamedValue aValue;
536 if ( rArg >>= aValue )
538 if ( aValue.Name == "BoundCell" )
540 if ( aValue.Value >>= aAddress )
542 bFoundAddress = true;
543 break;
549 if ( !bFoundAddress )
550 throw RuntimeException(u"Cell not found"_ustr, getXWeak());
552 // get the cell object
555 // first the sheets collection
556 Reference< XIndexAccess > xSheets;
557 if ( m_xDocument.is() )
558 xSheets.set(m_xDocument->getSheets( ), css::uno::UNO_QUERY);
559 OSL_ENSURE( xSheets.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
561 if ( xSheets.is() )
563 // the concrete sheet
564 Reference< XCellRange > xSheet(xSheets->getByIndex( aAddress.Sheet ), UNO_QUERY);
565 OSL_ENSURE( xSheet.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
567 // the concrete cell
568 if ( xSheet.is() )
570 m_xCell.set(xSheet->getCellByPosition( aAddress.Column, aAddress.Row ));
571 Reference< XCellAddressable > xAddressAccess( m_xCell, UNO_QUERY );
572 OSL_ENSURE( xAddressAccess.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
576 catch( const Exception& )
578 TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
581 if ( !m_xCell.is() )
582 throw RuntimeException(u"Failed to retrieve cell object"_ustr, getXWeak());
584 m_xCellText.set(m_xCell, css::uno::UNO_QUERY);
586 Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
587 if ( xBroadcaster.is() )
589 xBroadcaster->addModifyListener( this );
592 // TODO: add as XEventListener to the cell, so we get notified when it dies,
593 // and can dispose ourself then
595 // TODO: somehow add as listener so we get notified when the address of the cell changes
596 // We need to forward this as change in our BoundCell property to our property change listeners
598 // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
599 // to the BindableValue which is/will be bound to this instance.
601 m_bInitialized = true;
602 // TODO: place your code here
605 } // namespace calc
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */