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