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