LibreOffice Module sc (master) 1
cellvaluebinding.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
37#include <comphelper/types.hxx>
39
40namespace calc
41{
42
43#define PROP_HANDLE_BOUND_CELL 1
44
45 namespace lang = ::com::sun::star::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;
55
56 OCellValueBinding::OCellValueBinding( const Reference< XSpreadsheetDocument >& _rxDocument, bool _bListPos )
59 ,m_xDocument( _rxDocument )
60 ,m_aModifyListeners( m_aMutex )
61 ,m_bInitialized( false )
62 ,m_bListPos( _bListPos )
63 {
64 // register our property at the base class
65 registerPropertyNoMember(
66 "BoundCell",
68 PropertyAttribute::BOUND | PropertyAttribute::READONLY,
70 css::uno::Any(CellAddress())
71 );
72
73 // TODO: implement a ReadOnly property as required by the service,
74 // which probably maps to the cell being locked
75 }
76
77 OCellValueBinding::~OCellValueBinding( )
78 {
79 if ( !OCellValueBinding_Base::rBHelper.bDisposed )
80 {
81 acquire(); // prevent duplicate dtor
82 dispose();
83 }
84 }
85
87
89
90 void SAL_CALL OCellValueBinding::disposing()
91 {
92 Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
93 if ( xBroadcaster.is() )
94 {
95 xBroadcaster->removeModifyListener( this );
96 }
97
98 WeakComponentImplHelperBase::disposing();
99
100 // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
101 // for the cell)
102 }
103
104 Reference< XPropertySetInfo > SAL_CALL OCellValueBinding::getPropertySetInfo( )
105 {
106 return createPropertySetInfo( getInfoHelper() ) ;
107 }
108
109 ::cppu::IPropertyArrayHelper& SAL_CALL OCellValueBinding::getInfoHelper()
110 {
111 return *OCellValueBinding_PABase::getArrayHelper();
112 }
113
114 ::cppu::IPropertyArrayHelper* OCellValueBinding::createArrayHelper( ) const
115 {
116 Sequence< Property > aProps;
117 describeProperties( aProps );
118 return new ::cppu::OPropertyArrayHelper(aProps);
119 }
120
121 void SAL_CALL OCellValueBinding::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
122 {
123 OSL_ENSURE( _nHandle == PROP_HANDLE_BOUND_CELL, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
124 // we only have this one property...
125
126 _rValue.clear();
127 Reference< XCellAddressable > xCellAddress( m_xCell, UNO_QUERY );
128 if ( xCellAddress.is() )
129 _rValue <<= xCellAddress->getCellAddress( );
130 }
131
132 Sequence< Type > SAL_CALL OCellValueBinding::getSupportedValueTypes( )
133 {
134 checkDisposed( );
135 checkInitialized( );
136
137 sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
138 if ( m_bListPos )
139 ++nCount;
140
141 Sequence< Type > aTypes( nCount );
142 if ( m_xCell.is() )
143 {
144 auto pTypes = aTypes.getArray();
145
146 // an XCell can be used to set/get "double" values
147 pTypes[0] = ::cppu::UnoType<double>::get();
148 if ( m_xCellText.is() )
149 {
150 // an XTextRange can be used to set/get "string" values
151 pTypes[1] = ::cppu::UnoType<OUString>::get();
152 // and additionally, we use it to handle booleans
153 pTypes[2] = ::cppu::UnoType<sal_Bool>::get();
154 }
155
156 // add sal_Int32 only if constructed as ListPositionCellBinding
157 if ( m_bListPos )
159 }
160
161 return aTypes;
162 }
163
164 sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType )
165 {
166 checkDisposed( );
167 checkInitialized( );
168
169 // look up in our sequence
170 const Sequence< Type > aSupportedTypes( getSupportedValueTypes() );
171 for ( auto const & i : aSupportedTypes )
172 if ( aType == i )
173 return true;
174
175 return false;
176 }
177
178 Any SAL_CALL OCellValueBinding::getValue( const Type& aType )
179 {
180 checkDisposed( );
181 checkInitialized( );
182 checkValueType( aType );
183
184 Any aReturn;
185 switch ( aType.getTypeClass() )
186 {
187 case TypeClass_STRING:
188 OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::getValue: don't have a text!" );
189 if ( m_xCellText.is() )
190 aReturn <<= m_xCellText->getString();
191 else
192 aReturn <<= OUString();
193 break;
194
195 case TypeClass_BOOLEAN:
196 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
197 if ( m_xCell.is() )
198 {
199 // check if the cell has a numeric value (this might go into a helper function):
200
201 bool bHasValue = false;
202 CellContentType eCellType = m_xCell->getType();
203 if ( eCellType == CellContentType_VALUE )
204 bHasValue = true;
205 else if ( eCellType == CellContentType_FORMULA )
206 {
207 // check if the formula result is a value
208 if ( m_xCell->getError() == 0 )
209 {
210 Reference<XPropertySet> xProp( m_xCell, UNO_QUERY );
211 if ( xProp.is() )
212 {
213 sal_Int32 nResultType;
214 if ( (xProp->getPropertyValue("FormulaResultType2") >>= nResultType)
215 && nResultType == FormulaResult::VALUE )
216 bHasValue = true;
217 }
218 }
219 }
220
221 if ( bHasValue )
222 {
223 // 0 is "unchecked", any other value is "checked", regardless of number format
224 double nCellValue = m_xCell->getValue();
225 bool bBoolValue = ( nCellValue != 0.0 );
226 aReturn <<= bBoolValue;
227 }
228 // empty cells, text cells and text or error formula results: leave return value empty
229 }
230 break;
231
232 case TypeClass_DOUBLE:
233 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
234 if ( m_xCell.is() )
235 aReturn <<= m_xCell->getValue();
236 else
237 aReturn <<= double(0);
238 break;
239
240 case TypeClass_LONG:
241 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
242 if ( m_xCell.is() )
243 {
244 // The list position value in the cell is 1-based.
245 // We subtract 1 from any cell value (no special handling for 0 or negative values).
246
247 sal_Int32 nValue = static_cast<sal_Int32>(rtl::math::approxFloor( m_xCell->getValue() ));
248 --nValue;
249
250 aReturn <<= nValue;
251 }
252 else
253 aReturn <<= sal_Int32(0);
254 break;
255
256 default:
257 OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
258 // a type other than double and string should never have survived the checkValueType
259 // above
260 }
261 return aReturn;
262 }
263
264 void SAL_CALL OCellValueBinding::setValue( const Any& aValue )
265 {
266 checkDisposed( );
267 checkInitialized( );
268 if ( aValue.hasValue() )
269 checkValueType( aValue.getValueType() );
270
271 switch ( aValue.getValueType().getTypeClass() )
272 {
273 case TypeClass_STRING:
274 {
275 OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::setValue: don't have a text!" );
276
277 OUString sText;
278 aValue >>= sText;
279 if ( m_xCellText.is() )
280 m_xCellText->setString( sText );
281 }
282 break;
283
284 case TypeClass_BOOLEAN:
285 {
286 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
287
288 // boolean is stored as values 0 or 1
289 // TODO: set the number format to boolean if no format is set?
290
291 bool bValue( false );
292 aValue >>= bValue;
293 double nCellValue = bValue ? 1.0 : 0.0;
294
295 if ( m_xCell.is() )
296 m_xCell->setValue( nCellValue );
297
298 setBooleanFormat();
299 }
300 break;
301
302 case TypeClass_DOUBLE:
303 {
304 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
305
306 double nValue = 0;
307 aValue >>= nValue;
308 if ( m_xCell.is() )
309 m_xCell->setValue( nValue );
310 }
311 break;
312
313 case TypeClass_LONG:
314 {
315 OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
316
317 sal_Int32 nValue = 0;
318 aValue >>= nValue; // list index from control layer (0-based)
319 ++nValue; // the list position value in the cell is 1-based
320 if ( m_xCell.is() )
321 m_xCell->setValue( nValue );
322 }
323 break;
324
325 case TypeClass_VOID:
326 {
327 // #N/A error value can only be set using XCellRangeData
328
329 Reference<XCellRangeData> xData( m_xCell, UNO_QUERY );
330 OSL_ENSURE( xData.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
331 if ( xData.is() )
332 {
333 Sequence<Any> aInner(1); // one empty element
334 Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
335 xData->setDataArray( aOuter );
336 }
337 }
338 break;
339
340 default:
341 OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
342 // a type other than double and string should never have survived the checkValueType
343 // above
344 }
345 }
346
347 void OCellValueBinding::setBooleanFormat()
348 {
349 // set boolean number format if not already set
350
351 OUString sPropName( "NumberFormat" );
352 Reference<XPropertySet> xCellProp( m_xCell, UNO_QUERY );
353 Reference<XNumberFormatsSupplier> xSupplier( m_xDocument, UNO_QUERY );
354 if ( !(xSupplier.is() && xCellProp.is()) )
355 return;
356
357 Reference<XNumberFormats> xFormats(xSupplier->getNumberFormats());
358 Reference<XNumberFormatTypes> xTypes( xFormats, UNO_QUERY );
359 if ( !xTypes.is() )
360 return;
361
362 lang::Locale aLocale;
363 bool bWasBoolean = false;
364
365 sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) );
366 Reference<XPropertySet> xOldFormat;
367 try
368 {
369 xOldFormat.set(xFormats->getByKey( nOldIndex ));
370 }
371 catch ( Exception& )
372 {
373 // non-existing format - can happen, use defaults
374 }
375 if ( xOldFormat.is() )
376 {
377 // use the locale of the existing format
378 xOldFormat->getPropertyValue("Locale") >>= aLocale;
379
380 sal_Int16 nOldType = ::comphelper::getINT16(
381 xOldFormat->getPropertyValue("Type") );
382 if ( nOldType & NumberFormat::LOGICAL )
383 bWasBoolean = true;
384 }
385
386 if ( !bWasBoolean )
387 {
388 sal_Int32 nNewIndex = xTypes->getStandardFormat( NumberFormat::LOGICAL, aLocale );
389 xCellProp->setPropertyValue( sPropName, Any( nNewIndex ) );
390 }
391 }
392
393 void OCellValueBinding::checkDisposed( ) const
394 {
395 if ( OCellValueBinding_Base::rBHelper.bInDispose || OCellValueBinding_Base::rBHelper.bDisposed )
396 throw DisposedException();
397 // TODO: is it worth having an error message here?
398 }
399
400 void OCellValueBinding::checkInitialized()
401 {
402 if ( !m_bInitialized )
403 throw NotInitializedException("CellValueBinding is not initialized", getXWeak());
404 }
405
406 void OCellValueBinding::checkValueType( const Type& _rType ) const
407 {
408 OCellValueBinding* pNonConstThis = const_cast< OCellValueBinding* >( this );
409 if ( !pNonConstThis->supportsType( _rType ) )
410 {
411 OUString sMessage = "The given type (" +
412 _rType.getTypeName() +
413 ") is not supported by this binding.";
414 // TODO: localize this error message
415
416 throw IncompatibleTypesException( sMessage, *pNonConstThis );
417 // TODO: alternatively use a type converter service for this?
418 }
419 }
420
421 OUString SAL_CALL OCellValueBinding::getImplementationName( )
422 {
423 return "com.sun.star.comp.sheet.OCellValueBinding";
424 }
425
426 sal_Bool SAL_CALL OCellValueBinding::supportsService( const OUString& _rServiceName )
427 {
428 return cppu::supportsService(this, _rServiceName);
429 }
430
431 Sequence< OUString > SAL_CALL OCellValueBinding::getSupportedServiceNames( )
432 {
433 Sequence< OUString > aServices( m_bListPos ? 3 : 2 );
434 auto pServices = aServices.getArray();
435 pServices[ 0 ] = "com.sun.star.table.CellValueBinding";
436 pServices[ 1 ] = "com.sun.star.form.binding.ValueBinding";
437 if ( m_bListPos )
438 pServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
439 return aServices;
440 }
441
442 void SAL_CALL OCellValueBinding::addModifyListener( const Reference< XModifyListener >& _rxListener )
443 {
444 if ( _rxListener.is() )
445 m_aModifyListeners.addInterface( _rxListener );
446 }
447
448 void SAL_CALL OCellValueBinding::removeModifyListener( const Reference< XModifyListener >& _rxListener )
449 {
450 if ( _rxListener.is() )
451 m_aModifyListeners.removeInterface( _rxListener );
452 }
453
454 void OCellValueBinding::notifyModified()
455 {
456 EventObject aEvent;
457 aEvent.Source.set(*this);
458
459 ::comphelper::OInterfaceIteratorHelper3 aIter( m_aModifyListeners );
460 while ( aIter.hasMoreElements() )
461 {
462 try
463 {
464 aIter.next()->modified( aEvent );
465 }
466 catch( const RuntimeException& )
467 {
468 // silent this
469 }
470 catch( const Exception& )
471 {
472 TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
473 }
474 }
475 }
476
477 void SAL_CALL OCellValueBinding::modified( const EventObject& /* aEvent */ )
478 {
479 notifyModified();
480 }
481
482 void SAL_CALL OCellValueBinding::disposing( const EventObject& aEvent )
483 {
484 Reference<XInterface> xCellInt( m_xCell, UNO_QUERY );
485 if ( xCellInt == aEvent.Source )
486 {
487 // release references to cell object
488 m_xCell.clear();
489 m_xCellText.clear();
490 }
491 }
492
493 void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments )
494 {
495 if ( m_bInitialized )
496 throw RuntimeException("CellValueBinding is already initialized", getXWeak());
497
498 // get the cell address
499 CellAddress aAddress;
500 bool bFoundAddress = false;
501
502 for ( const Any& rArg : _rArguments )
503 {
504 NamedValue aValue;
505 if ( rArg >>= aValue )
506 {
507 if ( aValue.Name == "BoundCell" )
508 {
509 if ( aValue.Value >>= aAddress )
510 {
511 bFoundAddress = true;
512 break;
513 }
514 }
515 }
516 }
517
518 if ( !bFoundAddress )
519 throw RuntimeException("Cell not found", getXWeak());
520
521 // get the cell object
522 try
523 {
524 // first the sheets collection
525 Reference< XIndexAccess > xSheets;
526 if ( m_xDocument.is() )
527 xSheets.set(m_xDocument->getSheets( ), css::uno::UNO_QUERY);
528 OSL_ENSURE( xSheets.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
529
530 if ( xSheets.is() )
531 {
532 // the concrete sheet
533 Reference< XCellRange > xSheet(xSheets->getByIndex( aAddress.Sheet ), UNO_QUERY);
534 OSL_ENSURE( xSheet.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
535
536 // the concrete cell
537 if ( xSheet.is() )
538 {
539 m_xCell.set(xSheet->getCellByPosition( aAddress.Column, aAddress.Row ));
540 Reference< XCellAddressable > xAddressAccess( m_xCell, UNO_QUERY );
541 OSL_ENSURE( xAddressAccess.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
542 }
543 }
544 }
545 catch( const Exception& )
546 {
547 TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
548 }
549
550 if ( !m_xCell.is() )
551 throw RuntimeException("Failed to retrieve cell object", getXWeak());
552
553 m_xCellText.set(m_xCell, css::uno::UNO_QUERY);
554
555 Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
556 if ( xBroadcaster.is() )
557 {
558 xBroadcaster->addModifyListener( this );
559 }
560
561 // TODO: add as XEventListener to the cell, so we get notified when it dies,
562 // and can dispose ourself then
563
564 // TODO: somehow add as listener so we get notified when the address of the cell changes
565 // We need to forward this as change in our BoundCell property to our property change listeners
566
567 // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
568 // to the BindableValue which is/will be bound to this instance.
569
570 m_bInitialized = true;
571 // TODO: place your code here
572 }
573
574} // namespace calc
575
576/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AnyEventRef aEvent
#define PROP_HANDLE_BOUND_CELL
virtual sal_Bool SAL_CALL supportsType(const css::uno::Type &aType) override
OCellValueBinding(const css::uno::Reference< css::sheet::XSpreadsheetDocument > &_rxDocument, bool _bListPos)
constructed as ListPositionCellBinding?
css::uno::Reference< ListenerT > const & next()
css::uno::Type const & get()
int nCount
Reference< XOfficeDatabaseDocument > m_xDocument
#define TOOLS_WARN_EXCEPTION(area, stream)
sal_Int16 nValue
std::mutex m_aMutex
::cppu::WeakComponentImplHelper< css::form::binding::XValueBinding, css::lang::XServiceInfo, css::util::XModifyBroadcaster, css::util::XModifyListener, css::lang::XInitialization > OCellValueBinding_Base
::comphelper::OPropertyContainer OCellValueBinding_PBase
@ Exception
Type
void checkDisposed(bool _bThrow)
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
int i
const Supported_NumberingType aSupportedTypes[]
IMPLEMENT_FORWARD_XTYPEPROVIDER2(ChildWindowPane, ChildWindowPaneInterfaceBase, Pane)
IMPLEMENT_FORWARD_XINTERFACE2(ChildWindowPane, ChildWindowPaneInterfaceBase, Pane)
void dispose()
OUString sMessage
unsigned char sal_Bool
const SvXMLTokenMapEntry aTypes[]