1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmloff.hxx"
30 #include "formcellbinding.hxx"
31 #include <com/sun/star/form/binding/XBindableValue.hpp>
32 #include <com/sun/star/form/binding/XListEntrySink.hpp>
33 #include <com/sun/star/form/XGridColumnFactory.hpp>
34 #include <com/sun/star/frame/XModel.hpp>
35 #include <com/sun/star/container/XChild.hpp>
36 #include <com/sun/star/container/XNamed.hpp>
37 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
38 #include <com/sun/star/table/XCellRange.hpp>
39 #include <com/sun/star/form/XFormsSupplier.hpp>
40 #include <com/sun/star/form/XForm.hpp>
41 #include <com/sun/star/lang/XServiceInfo.hpp>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/beans/NamedValue.hpp>
44 #include "strings.hxx"
45 #include <osl/diagnose.h>
46 #include <rtl/logfile.hxx>
47 
48 #include <functional>
49 #include <algorithm>
50 
51 //............................................................................
52 namespace xmloff
53 {
54 //............................................................................
55 
56     using namespace ::com::sun::star::uno;
57     using namespace ::com::sun::star::beans;
58     using namespace ::com::sun::star::frame;
59     using namespace ::com::sun::star::sheet;
60     using namespace ::com::sun::star::container;
61     using namespace ::com::sun::star::drawing;
62     using namespace ::com::sun::star::table;
63     using namespace ::com::sun::star::form;
64     using namespace ::com::sun::star::lang;
65     using namespace ::com::sun::star::form::binding;
66 
67 namespace
68 {
69     using ::com::sun::star::uno::Reference;
70     using ::com::sun::star::uno::XInterface;
71     using ::com::sun::star::container::XChild;
72     using ::com::sun::star::frame::XModel;
73     using ::com::sun::star::uno::UNO_QUERY;
74 
75 	//....................................................................
76     template< class TYPE >
77     Reference< TYPE > getTypedModelNode( const Reference< XInterface >& _rxModelNode )
78     {
79         Reference< TYPE > xTypedNode( _rxModelNode, UNO_QUERY );
80         if ( xTypedNode.is() )
81 	        return xTypedNode;
82         else
83         {
84 	        Reference< XChild > xChild( _rxModelNode, UNO_QUERY );
85 	        if ( xChild.is() )
86 		        return getTypedModelNode< TYPE >( xChild->getParent() );
87 	        else
88 		        return NULL;
89         }
90     }
91 
92 	//....................................................................
93     Reference< XModel > getDocument( const Reference< XInterface >& _rxModelNode )
94     {
95         return getTypedModelNode< XModel >( _rxModelNode );
96     }
97 
98     //....................................................................
99     struct StringCompare : public ::std::unary_function< ::rtl::OUString, bool >
100     {
101     private:
102         const ::rtl::OUString m_sReference;
103 
104     public:
105         StringCompare( const ::rtl::OUString& _rReference ) : m_sReference( _rReference ) { }
106 
107         inline bool operator()( const ::rtl::OUString& _rCompare )
108         {
109             return ( _rCompare == m_sReference );
110         }
111     };
112 }
113 
114 //========================================================================
115 //= FormCellBindingHelper
116 //========================================================================
117 //------------------------------------------------------------------------
118 FormCellBindingHelper::FormCellBindingHelper( const Reference< XPropertySet >& _rxControlModel, const Reference< XModel >& _rxDocument )
119     :m_xControlModel( _rxControlModel )
120     ,m_xDocument( _rxDocument, UNO_QUERY )
121 {
122     OSL_ENSURE( m_xControlModel.is(), "FormCellBindingHelper::FormCellBindingHelper: invalid control model!" );
123 
124     if ( !m_xDocument.is() )
125         m_xDocument = m_xDocument.query( getDocument( m_xControlModel ) );
126     OSL_ENSURE( m_xDocument.is(), "FormCellBindingHelper::FormCellBindingHelper: Did not find the spreadsheet document!" );
127 }
128 
129 //------------------------------------------------------------------------
130 sal_Bool FormCellBindingHelper::livesInSpreadsheetDocument( const Reference< XPropertySet >& _rxControlModel )
131 {
132     Reference< XSpreadsheetDocument > xDocument( getDocument( _rxControlModel ), UNO_QUERY );
133     return xDocument.is();
134 }
135 
136 //------------------------------------------------------------------------
137 bool FormCellBindingHelper::convertStringAddress( const ::rtl::OUString& _rAddressDescription, CellAddress& /* [out] */ _rAddress, sal_Int16 /*_nAssumeSheet*/ ) const
138 {
139     Any aAddress;
140     return doConvertAddressRepresentations(
141                 PROPERTY_FILE_REPRESENTATION,
142                 makeAny( _rAddressDescription ),
143                 PROPERTY_ADDRESS,
144                 aAddress,
145                 false
146            )
147        &&  ( aAddress >>= _rAddress );
148 }
149 
150 //------------------------------------------------------------------------
151 bool FormCellBindingHelper::convertStringAddress( const ::rtl::OUString& _rAddressDescription,
152                         CellRangeAddress& /* [out] */ _rAddress ) const
153 {
154     Any aAddress;
155     return doConvertAddressRepresentations(
156                 PROPERTY_FILE_REPRESENTATION,
157                 makeAny( _rAddressDescription ),
158                 PROPERTY_ADDRESS,
159                 aAddress,
160                 true
161            )
162        &&  ( aAddress >>= _rAddress );
163 }
164 
165 //------------------------------------------------------------------------
166 Reference< XValueBinding > FormCellBindingHelper::createCellBindingFromStringAddress( const ::rtl::OUString& _rAddress, bool _bUseIntegerBinding ) const
167 {
168     Reference< XValueBinding > xBinding;
169     if ( !m_xDocument.is() )
170         // very bad ...
171         return xBinding;
172 
173     // get the UNO representation of the address
174     CellAddress aAddress;
175     if ( !_rAddress.getLength() || !convertStringAddress( _rAddress, aAddress ) )
176         return xBinding;
177 
178     xBinding = xBinding.query( createDocumentDependentInstance(
179         _bUseIntegerBinding ? SERVICE_LISTINDEXCELLBINDING : SERVICE_CELLVALUEBINDING,
180         PROPERTY_BOUND_CELL,
181         makeAny( aAddress )
182     ) );
183 
184     return xBinding;
185 }
186 
187 //------------------------------------------------------------------------
188 Reference< XListEntrySource > FormCellBindingHelper::createCellListSourceFromStringAddress( const ::rtl::OUString& _rAddress ) const
189 {
190     Reference< XListEntrySource > xSource;
191 
192     CellRangeAddress aRangeAddress;
193     if ( !convertStringAddress( _rAddress, aRangeAddress ) )
194         return xSource;
195 
196     // create a range object for this address
197     xSource = xSource.query( createDocumentDependentInstance(
198         SERVICE_CELLRANGELISTSOURCE,
199         PROPERTY_LIST_CELL_RANGE,
200         makeAny( aRangeAddress )
201     ) );
202 
203     return xSource;
204 }
205 
206 //------------------------------------------------------------------------
207 ::rtl::OUString FormCellBindingHelper::getStringAddressFromCellBinding( const Reference< XValueBinding >& _rxBinding ) const
208 {
209     OSL_PRECOND( !_rxBinding.is() || isCellBinding( _rxBinding ), "FormCellBindingHelper::getStringAddressFromCellBinding: this is no cell binding!" );
210 
211     ::rtl::OUString sAddress;
212     try
213     {
214         Reference< XPropertySet > xBindingProps( _rxBinding, UNO_QUERY );
215         OSL_ENSURE( xBindingProps.is() || !_rxBinding.is(), "FormCellBindingHelper::getStringAddressFromCellBinding: no property set for the binding!" );
216         if ( xBindingProps.is() )
217         {
218             CellAddress aAddress;
219             xBindingProps->getPropertyValue( PROPERTY_BOUND_CELL ) >>= aAddress;
220 
221             Any aStringAddress;
222             doConvertAddressRepresentations( PROPERTY_ADDRESS, makeAny( aAddress ),
223                 PROPERTY_FILE_REPRESENTATION, aStringAddress, false );
224 
225             aStringAddress >>= sAddress;
226         }
227     }
228     catch( const Exception& )
229     {
230         OSL_ENSURE( sal_False, "FormCellBindingHelper::getStringAddressFromCellBinding: caught an exception!" );
231     }
232 
233     return sAddress;
234 }
235 
236 //------------------------------------------------------------------------
237 ::rtl::OUString FormCellBindingHelper::getStringAddressFromCellListSource( const Reference< XListEntrySource >& _rxSource ) const
238 {
239     OSL_PRECOND( !_rxSource.is() || isCellRangeListSource( _rxSource ), "FormCellBindingHelper::getStringAddressFromCellListSource: this is no cell list source!" );
240 
241     ::rtl::OUString sAddress;
242     try
243     {
244         Reference< XPropertySet > xSourceProps( _rxSource, UNO_QUERY );
245         OSL_ENSURE( xSourceProps.is() || !_rxSource.is(), "FormCellBindingHelper::getStringAddressFromCellListSource: no property set for the list source!" );
246         if ( xSourceProps.is() )
247         {
248             CellRangeAddress aRangeAddress;
249             xSourceProps->getPropertyValue( PROPERTY_LIST_CELL_RANGE ) >>= aRangeAddress;
250 
251             Any aStringAddress;
252             doConvertAddressRepresentations( PROPERTY_ADDRESS, makeAny( aRangeAddress ),
253                 PROPERTY_FILE_REPRESENTATION, aStringAddress, true );
254             aStringAddress >>= sAddress;
255         }
256     }
257     catch( const Exception& )
258     {
259         OSL_ENSURE( sal_False, "FormCellBindingHelper::getStringAddressFromCellListSource: caught an exception!" );
260     }
261 
262     return sAddress;
263 }
264 
265 //------------------------------------------------------------------------
266 bool FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies( const Reference< XSpreadsheetDocument >& _rxDocument, const ::rtl::OUString& _rService ) SAL_THROW(())
267 {
268     bool bYesItIs = false;
269 
270     try
271     {
272         Reference< XServiceInfo > xSI( _rxDocument, UNO_QUERY );
273         if ( xSI.is() && xSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) )
274         {
275             Reference< XMultiServiceFactory > xDocumentFactory( _rxDocument, UNO_QUERY );
276             OSL_ENSURE( xDocumentFactory.is(), "FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies: spreadsheet document, but no factory?" );
277 
278             Sequence< ::rtl::OUString > aAvailableServices;
279             if ( xDocumentFactory.is() )
280                 aAvailableServices = xDocumentFactory->getAvailableServiceNames( );
281 
282             const ::rtl::OUString* pFound = ::std::find_if(
283                 aAvailableServices.getConstArray(),
284                 aAvailableServices.getConstArray() + aAvailableServices.getLength(),
285                 StringCompare( _rService )
286             );
287             if ( pFound - aAvailableServices.getConstArray() < aAvailableServices.getLength() )
288             {
289                 bYesItIs = true;
290             }
291         }
292     }
293     catch( const Exception& )
294     {
295         OSL_ENSURE( sal_False, "FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies: caught an exception!" );
296     }
297 
298     return bYesItIs;
299 }
300 
301 //------------------------------------------------------------------------
302 bool FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies( const ::rtl::OUString& _rService ) const SAL_THROW(())
303 {
304     return isSpreadsheetDocumentWhichSupplies( m_xDocument, _rService );
305 }
306 
307 //------------------------------------------------------------------------
308 bool FormCellBindingHelper::isListCellRangeAllowed( const Reference< XModel >& _rxDocument )
309 {
310     return isSpreadsheetDocumentWhichSupplies(
311         Reference< XSpreadsheetDocument >( _rxDocument, UNO_QUERY ),
312         SERVICE_CELLRANGELISTSOURCE
313     );
314 }
315 
316 //------------------------------------------------------------------------
317 bool FormCellBindingHelper::isListCellRangeAllowed( ) const
318 {
319     bool bAllow( false );
320 
321     Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
322     if ( xSink.is() )
323     {
324         bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_CELLRANGELISTSOURCE );
325     }
326 
327     return bAllow;
328 }
329 
330 //------------------------------------------------------------------------
331 bool FormCellBindingHelper::isCellBindingAllowed( ) const
332 {
333     bool bAllow( false );
334 
335     Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
336     if ( xBindable.is() )
337     {
338         // the control can potentially be bound to an external value
339         // Does it live within a Calc document, and is able to supply CellBindings?
340         bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_CELLVALUEBINDING );
341     }
342 
343     return bAllow;
344 }
345 
346 //------------------------------------------------------------------------
347 bool FormCellBindingHelper::isCellBindingAllowed( const Reference< XModel >& _rxDocument )
348 {
349     return isSpreadsheetDocumentWhichSupplies(
350         Reference< XSpreadsheetDocument >( _rxDocument, UNO_QUERY ),
351         SERVICE_CELLVALUEBINDING
352     );
353 }
354 
355 //------------------------------------------------------------------------
356 bool FormCellBindingHelper::isCellBinding( const Reference< XValueBinding >& _rxBinding ) const
357 {
358     return doesComponentSupport( _rxBinding.get(), SERVICE_CELLVALUEBINDING );
359 }
360 
361 //------------------------------------------------------------------------
362 bool FormCellBindingHelper::isCellIntegerBinding( const Reference< XValueBinding >& _rxBinding ) const
363 {
364     return doesComponentSupport( _rxBinding.get(), SERVICE_LISTINDEXCELLBINDING );
365 }
366 
367 //------------------------------------------------------------------------
368 bool FormCellBindingHelper::isCellRangeListSource( const Reference< XListEntrySource >& _rxSource ) const
369 {
370     return doesComponentSupport( _rxSource.get(), SERVICE_CELLRANGELISTSOURCE );
371 }
372 
373 //------------------------------------------------------------------------
374 bool FormCellBindingHelper::doesComponentSupport( const Reference< XInterface >& _rxComponent, const ::rtl::OUString& _rService ) const
375 {
376     bool bDoes = false;
377     Reference< XServiceInfo > xSI( _rxComponent, UNO_QUERY );
378     bDoes = xSI.is() && xSI->supportsService( _rService );
379     return bDoes;
380 }
381 
382 //------------------------------------------------------------------------
383 Reference< XValueBinding > FormCellBindingHelper::getCurrentBinding( ) const
384 {
385     Reference< XValueBinding > xBinding;
386     Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
387     if ( xBindable.is() )
388         xBinding = xBindable->getValueBinding();
389     return xBinding;
390 }
391 
392 //------------------------------------------------------------------------
393 Reference< XListEntrySource > FormCellBindingHelper::getCurrentListSource( ) const
394 {
395     Reference< XListEntrySource > xSource;
396     Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
397     if ( xSink.is() )
398         xSource = xSink->getListEntrySource();
399     return xSource;
400 }
401 
402 //------------------------------------------------------------------------
403 void FormCellBindingHelper::setBinding( const Reference< XValueBinding >& _rxBinding )
404 {
405     Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
406     OSL_PRECOND( xBindable.is(), "FormCellBindingHelper::setBinding: the object is not bindable!" );
407     if ( xBindable.is() )
408         xBindable->setValueBinding( _rxBinding );
409 }
410 
411 //------------------------------------------------------------------------
412 void FormCellBindingHelper::setListSource( const Reference< XListEntrySource >& _rxSource )
413 {
414     Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
415     OSL_PRECOND( xSink.is(), "FormCellBindingHelper::setListSource: the object is no list entry sink!" );
416     if ( xSink.is() )
417         xSink->setListEntrySource( _rxSource );
418 }
419 
420 //------------------------------------------------------------------------
421 Reference< XInterface > FormCellBindingHelper::createDocumentDependentInstance( const ::rtl::OUString& _rService, const ::rtl::OUString& _rArgumentName,
422     const Any& _rArgumentValue ) const
423 {
424     Reference< XInterface > xReturn;
425 
426     Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY );
427     OSL_ENSURE( xDocumentFactory.is(), "FormCellBindingHelper::createDocumentDependentInstance: no document service factory!" );
428     if ( xDocumentFactory.is() )
429     {
430         try
431         {
432             if ( _rArgumentName.getLength() )
433             {
434                 NamedValue aArg;
435                 aArg.Name = _rArgumentName;
436                 aArg.Value = _rArgumentValue;
437 
438                 Sequence< Any > aArgs( 1 );
439                 aArgs[ 0 ] <<= aArg;
440 
441                 xReturn = xDocumentFactory->createInstanceWithArguments( _rService, aArgs );
442             }
443             else
444             {
445                 xReturn = xDocumentFactory->createInstance( _rService );
446             }
447         }
448         catch ( const Exception& )
449         {
450             OSL_ENSURE( sal_False, "FormCellBindingHelper::createDocumentDependentInstance: could not create the binding at the document!" );
451         }
452     }
453     return xReturn;
454 }
455 
456 //------------------------------------------------------------------------
457 bool FormCellBindingHelper::doConvertAddressRepresentations( const ::rtl::OUString& _rInputProperty, const Any& _rInputValue,
458     const ::rtl::OUString& _rOutputProperty, Any& _rOutputValue, bool _bIsRange ) const SAL_THROW(())
459 {
460     bool bSuccess = false;
461 
462     Reference< XPropertySet > xConverter(
463         createDocumentDependentInstance(
464             _bIsRange ? SERVICE_RANGEADDRESS_CONVERSION : SERVICE_ADDRESS_CONVERSION,
465             ::rtl::OUString(),
466             Any()
467         ),
468         UNO_QUERY
469     );
470     OSL_ENSURE( xConverter.is(), "FormCellBindingHelper::doConvertAddressRepresentations: could not get a converter service!" );
471     if ( xConverter.is() )
472     {
473         try
474         {
475             xConverter->setPropertyValue( _rInputProperty, _rInputValue );
476             _rOutputValue = xConverter->getPropertyValue( _rOutputProperty );
477             bSuccess = true;
478         }
479         catch( const Exception& )
480         {
481         	OSL_ENSURE( sal_False, "FormCellBindingHelper::doConvertAddressRepresentations: caught an exception!" );
482         }
483     }
484 
485     return bSuccess;
486 }
487 
488 //............................................................................
489 }   // namespace xmloff
490 //............................................................................
491