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_extensions.hxx"
30 #include "cellbindinghelper.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/FormComponentType.hpp>
34 #include <com/sun/star/form/XGridColumnFactory.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 <com/sun/star/sheet/XSpreadsheet.hpp>
45 #include <unotools/transliterationwrapper.hxx>
46 #include <osl/diagnose.h>
47 #include <tools/diagnose_ex.h>
48 #include "formstrings.hxx"
49 
50 #include <functional>
51 #include <algorithm>
52 
53 //............................................................................
54 namespace pcr
55 {
56 //............................................................................
57 
58     using namespace ::com::sun::star::uno;
59     using namespace ::com::sun::star::beans;
60     using namespace ::com::sun::star::frame;
61     using namespace ::com::sun::star::sheet;
62     using namespace ::com::sun::star::container;
63     using namespace ::com::sun::star::drawing;
64     using namespace ::com::sun::star::table;
65     using namespace ::com::sun::star::form;
66     using namespace ::com::sun::star::lang;
67     using namespace ::com::sun::star::i18n;
68     using namespace ::com::sun::star::form::binding;
69 
70     namespace
71     {
72         //....................................................................
73         struct StringCompare : public ::std::unary_function< ::rtl::OUString, bool >
74         {
75         private:
76             ::rtl::OUString m_sReference;
77 
78         public:
79             StringCompare( const ::rtl::OUString& _rReference ) : m_sReference( _rReference ) { }
80 
81             inline bool operator()( const ::rtl::OUString& _rCompare )
82             {
83                 return ( _rCompare == m_sReference ) ? true : false;
84             }
85         };
86     }
87 
88     //========================================================================
89     //= CellBindingHelper
90     //========================================================================
91     //------------------------------------------------------------------------
92     CellBindingHelper::CellBindingHelper( const Reference< XPropertySet >& _rxControlModel, const Reference< XModel >& _rxContextDocument )
93         :m_xControlModel( _rxControlModel )
94     {
95         OSL_ENSURE( m_xControlModel.is(), "CellBindingHelper::CellBindingHelper: invalid control model!" );
96 
97         m_xDocument = m_xDocument.query( _rxContextDocument );
98         OSL_ENSURE( m_xDocument.is(), "CellBindingHelper::CellBindingHelper: This is no spreadsheet document!" );
99 
100         OSL_ENSURE( isSpreadsheetDocumentWhichSupplies( SERVICE_ADDRESS_CONVERSION ),
101             "CellBindingHelper::CellBindingHelper: the document cannot convert address representations!" );
102     }
103 
104     //------------------------------------------------------------------------
105     sal_Bool CellBindingHelper::isSpreadsheetDocument( const Reference< XModel >& _rxContextDocument )
106     {
107         return Reference< XSpreadsheetDocument >::query( _rxContextDocument ).is();
108     }
109 
110     //------------------------------------------------------------------------
111     sal_Int16 CellBindingHelper::getControlSheetIndex( Reference< XSpreadsheet >& _out_rxSheet ) const
112     {
113         sal_Int16 nSheetIndex = -1;
114         // every sheet has a draw page, and every draw page has a forms collection.
115         // Our control, OTOH, belongs to a forms collection. Match these ...
116         try
117         {
118             // for determining the draw page, we need the forms collection which
119             // the object belongs to. This is the first object up the hierarchy which is
120             // *no* XForm (and, well, no XGridColumnFactory)
121             Reference< XChild > xCheck( m_xControlModel, UNO_QUERY );
122             Reference< XForm > xParentAsForm; if ( xCheck.is() ) xParentAsForm = xParentAsForm.query( xCheck->getParent() );
123             Reference< XGridColumnFactory > xParentAsGrid; if ( xCheck.is() ) xParentAsGrid = xParentAsGrid.query( xCheck->getParent() );
124 
125             while ( ( xParentAsForm.is() || xParentAsGrid.is() ) && xCheck.is() )
126             {
127                 xCheck = xCheck.query( xCheck->getParent() );
128                 xParentAsForm = xParentAsForm.query( xCheck.is() ? xCheck->getParent() : (Reference< XInterface >) Reference< XForm >() );
129                 xParentAsGrid = xParentAsGrid.query( xCheck.is() ? xCheck->getParent() : (Reference< XInterface >) Reference< XGridColumnFactory >() );
130             }
131             Reference< XInterface > xFormsCollection( xCheck.is() ? xCheck->getParent() : Reference< XInterface >() );
132 
133             // now iterate through the sheets
134             Reference< XIndexAccess > xSheets( m_xDocument->getSheets(), UNO_QUERY );
135             if ( xSheets.is() && xFormsCollection.is() )
136             {
137                 for ( sal_Int32 i = 0; i < xSheets->getCount(); ++i )
138                 {
139                     Reference< XDrawPageSupplier > xSuppPage( xSheets->getByIndex( i ), UNO_QUERY_THROW );
140                     Reference< XFormsSupplier > xSuppForms( xSuppPage->getDrawPage(), UNO_QUERY_THROW );
141 
142                     if ( xSuppForms->getForms() == xFormsCollection )
143                     {   // found it
144                         nSheetIndex = (sal_Int16)i;
145                         _out_rxSheet.set( xSuppPage, UNO_QUERY_THROW );
146                         break;
147                     }
148                 }
149             }
150         }
151         catch( const Exception& )
152         {
153             DBG_UNHANDLED_EXCEPTION();
154         }
155 
156         return nSheetIndex;
157     }
158 
159     //------------------------------------------------------------------------
160     bool CellBindingHelper::convertStringAddress( const ::rtl::OUString& _rAddressDescription, CellAddress& /* [out] */ _rAddress ) const
161     {
162         Any aAddress;
163         return doConvertAddressRepresentations(
164                     PROPERTY_UI_REPRESENTATION,
165                     makeAny( _rAddressDescription ),
166                     PROPERTY_ADDRESS,
167                     aAddress,
168                     false
169                )
170            &&  ( aAddress >>= _rAddress );
171     }
172 
173     //------------------------------------------------------------------------
174     bool CellBindingHelper::doConvertAddressRepresentations( const ::rtl::OUString& _rInputProperty, const Any& _rInputValue,
175         const ::rtl::OUString& _rOutputProperty, Any& _rOutputValue, bool _bIsRange ) const SAL_THROW(())
176     {
177         bool bSuccess = false;
178 
179         Reference< XPropertySet > xConverter(
180             createDocumentDependentInstance(
181                 _bIsRange ? SERVICE_RANGEADDRESS_CONVERSION : SERVICE_ADDRESS_CONVERSION,
182                 ::rtl::OUString(),
183                 Any()
184             ),
185             UNO_QUERY
186         );
187         OSL_ENSURE( xConverter.is(), "CellBindingHelper::doConvertAddressRepresentations: could not get a converter service!" );
188         if ( xConverter.is() )
189         {
190             try
191             {
192                 Reference< XSpreadsheet > xSheet;
193                 xConverter->setPropertyValue( PROPERTY_REFERENCE_SHEET, makeAny( (sal_Int32)getControlSheetIndex( xSheet ) ) );
194                 xConverter->setPropertyValue( _rInputProperty, _rInputValue );
195                 _rOutputValue = xConverter->getPropertyValue( _rOutputProperty );
196                 bSuccess = true;
197             }
198             catch( const Exception& )
199             {
200             	OSL_ENSURE( sal_False, "CellBindingHelper::doConvertAddressRepresentations: caught an exception!" );
201             }
202         }
203 
204         return bSuccess;
205     }
206 
207     //------------------------------------------------------------------------
208     bool CellBindingHelper::convertStringAddress( const ::rtl::OUString& _rAddressDescription,
209                             CellRangeAddress& /* [out] */ _rAddress ) const
210     {
211         Any aAddress;
212         return doConvertAddressRepresentations(
213                     PROPERTY_UI_REPRESENTATION,
214                     makeAny( _rAddressDescription ),
215                     PROPERTY_ADDRESS,
216                     aAddress,
217                     true
218                )
219            &&  ( aAddress >>= _rAddress );
220     }
221 
222     //------------------------------------------------------------------------
223     Reference< XValueBinding > CellBindingHelper::createCellBindingFromAddress( const CellAddress& _rAddress, bool _bSupportIntegerExchange ) const
224     {
225         Reference< XValueBinding > xBinding( createDocumentDependentInstance(
226             _bSupportIntegerExchange ? SERVICE_SHEET_CELL_INT_BINDING : SERVICE_SHEET_CELL_BINDING,
227             PROPERTY_BOUND_CELL,
228             makeAny( _rAddress )
229         ), UNO_QUERY );
230 
231         return xBinding;
232     }
233 
234     //------------------------------------------------------------------------
235     Reference< XValueBinding > CellBindingHelper::createCellBindingFromStringAddress( const ::rtl::OUString& _rAddress, bool _bSupportIntegerExchange ) const
236     {
237         Reference< XValueBinding > xBinding;
238         if ( !m_xDocument.is() )
239             // very bad ...
240             return xBinding;
241 
242         // get the UNO representation of the address
243         CellAddress aAddress;
244         if ( !_rAddress.getLength() || !convertStringAddress( _rAddress, aAddress ) )
245             return xBinding;
246 
247         return createCellBindingFromAddress( aAddress, _bSupportIntegerExchange );
248     }
249 
250     //------------------------------------------------------------------------
251     Reference< XListEntrySource > CellBindingHelper::createCellListSourceFromStringAddress( const ::rtl::OUString& _rAddress ) const
252     {
253         Reference< XListEntrySource > xSource;
254 
255         CellRangeAddress aRangeAddress;
256         if ( !_rAddress.getLength() || !convertStringAddress( _rAddress, aRangeAddress ) )
257             return xSource;
258 
259         // create a range object for this address
260         xSource = xSource.query( createDocumentDependentInstance(
261             SERVICE_SHEET_CELLRANGE_LISTSOURCE,
262             PROPERTY_LIST_CELL_RANGE,
263             makeAny( aRangeAddress )
264         ) );
265 
266         return xSource;
267     }
268 
269     //------------------------------------------------------------------------
270     Reference< XInterface > CellBindingHelper::createDocumentDependentInstance( const ::rtl::OUString& _rService, const ::rtl::OUString& _rArgumentName,
271         const Any& _rArgumentValue ) const
272     {
273         Reference< XInterface > xReturn;
274 
275         Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY );
276         OSL_ENSURE( xDocumentFactory.is(), "CellBindingHelper::createDocumentDependentInstance: no document service factory!" );
277         if ( xDocumentFactory.is() )
278         {
279             try
280             {
281                 if ( _rArgumentName.getLength() )
282                 {
283                     NamedValue aArg;
284                     aArg.Name = _rArgumentName;
285                     aArg.Value = _rArgumentValue;
286 
287                     Sequence< Any > aArgs( 1 );
288                     aArgs[ 0 ] <<= aArg;
289 
290                     xReturn = xDocumentFactory->createInstanceWithArguments( _rService, aArgs );
291                 }
292                 else
293                 {
294                     xReturn = xDocumentFactory->createInstance( _rService );
295                 }
296             }
297             catch ( const Exception& )
298             {
299                 OSL_ENSURE( sal_False, "CellBindingHelper::createDocumentDependentInstance: could not create the binding at the document!" );
300             }
301         }
302         return xReturn;
303     }
304 
305     //------------------------------------------------------------------------
306     bool CellBindingHelper::getAddressFromCellBinding(
307         const Reference< XValueBinding >& _rxBinding, CellAddress& _rAddress ) const
308     {
309         OSL_PRECOND( !_rxBinding.is() || isCellBinding( _rxBinding ), "CellBindingHelper::getAddressFromCellBinding: this is no cell binding!" );
310 
311         bool bReturn = false;
312         if ( !m_xDocument.is() )
313             // very bad ...
314             return bReturn;
315 
316         try
317         {
318             Reference< XPropertySet > xBindingProps( _rxBinding, UNO_QUERY );
319             OSL_ENSURE( xBindingProps.is() || !_rxBinding.is(), "CellBindingHelper::getAddressFromCellBinding: no property set for the binding!" );
320             if ( xBindingProps.is() )
321             {
322                 CellAddress aAddress;
323                 bReturn = (bool)( xBindingProps->getPropertyValue( PROPERTY_BOUND_CELL ) >>= _rAddress );
324             }
325         }
326         catch( const Exception& )
327         {
328             OSL_ENSURE( sal_False, "CellBindingHelper::getAddressFromCellBinding: caught an exception!" );
329         }
330 
331         return bReturn;
332     }
333 
334     //------------------------------------------------------------------------
335     ::rtl::OUString CellBindingHelper::getStringAddressFromCellBinding( const Reference< XValueBinding >& _rxBinding ) const
336     {
337         CellAddress aAddress;
338         ::rtl::OUString sAddress;
339         if ( getAddressFromCellBinding( _rxBinding, aAddress ) )
340         {
341             Any aStringAddress;
342             doConvertAddressRepresentations( PROPERTY_ADDRESS, makeAny( aAddress ),
343                 PROPERTY_UI_REPRESENTATION, aStringAddress, false );
344 
345             aStringAddress >>= sAddress;
346         }
347 
348         return sAddress;
349     }
350 
351     //------------------------------------------------------------------------
352     ::rtl::OUString CellBindingHelper::getStringAddressFromCellListSource( const Reference< XListEntrySource >& _rxSource ) const
353     {
354         OSL_PRECOND( !_rxSource.is() || isCellRangeListSource( _rxSource ), "CellBindingHelper::getStringAddressFromCellListSource: this is no cell list source!" );
355 
356         ::rtl::OUString sAddress;
357         if ( !m_xDocument.is() )
358             // very bad ...
359             return sAddress;
360 
361         try
362         {
363             Reference< XPropertySet > xSourceProps( _rxSource, UNO_QUERY );
364             OSL_ENSURE( xSourceProps.is() || !_rxSource.is(), "CellBindingHelper::getStringAddressFromCellListSource: no property set for the list source!" );
365             if ( xSourceProps.is() )
366             {
367                 CellRangeAddress aRangeAddress;
368                 xSourceProps->getPropertyValue( PROPERTY_LIST_CELL_RANGE ) >>= aRangeAddress;
369 
370                 Any aStringAddress;
371                 doConvertAddressRepresentations( PROPERTY_ADDRESS, makeAny( aRangeAddress ),
372                     PROPERTY_UI_REPRESENTATION, aStringAddress, true );
373                 aStringAddress >>= sAddress;
374             }
375         }
376         catch( const Exception& )
377         {
378             OSL_ENSURE( sal_False, "CellBindingHelper::getStringAddressFromCellListSource: caught an exception!" );
379         }
380 
381         return sAddress;
382     }
383 
384     //------------------------------------------------------------------------
385     bool CellBindingHelper::isSpreadsheetDocumentWhichSupplies( const ::rtl::OUString& _rService ) const
386     {
387         bool bYesItIs = false;
388 
389         Reference< XServiceInfo > xSI( m_xDocument, UNO_QUERY );
390         if ( xSI.is() && xSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) )
391         {
392             Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY );
393             OSL_ENSURE( xDocumentFactory.is(), "CellBindingHelper::isSpreadsheetDocumentWhichSupplies: spreadsheet document, but no factory?" );
394 
395             Sequence< ::rtl::OUString > aAvailableServices;
396             if ( xDocumentFactory.is() )
397                 aAvailableServices = xDocumentFactory->getAvailableServiceNames( );
398 
399             const ::rtl::OUString* pFound = ::std::find_if(
400                 aAvailableServices.getConstArray(),
401                 aAvailableServices.getConstArray() + aAvailableServices.getLength(),
402                 StringCompare( _rService )
403             );
404             if ( pFound - aAvailableServices.getConstArray() < aAvailableServices.getLength() )
405             {
406                 bYesItIs = true;
407             }
408         }
409 
410         return bYesItIs;
411     }
412 
413     //------------------------------------------------------------------------
414     bool CellBindingHelper::isListCellRangeAllowed( ) const
415     {
416         bool bAllow( false );
417 
418         Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
419         if ( xSink.is() )
420         {
421             bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELLRANGE_LISTSOURCE );
422         }
423 
424         return bAllow;
425     }
426 
427     //------------------------------------------------------------------------
428     bool CellBindingHelper::isCellIntegerBindingAllowed( ) const
429     {
430         bool bAllow( true );
431 
432         // first, we only offer this for controls which allow bindings in general
433         Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
434         if ( !xBindable.is() )
435             bAllow = false;
436 
437         // then, we must live in a spreadsheet document which can provide the special
438         // service needed for exchanging integer values
439         if ( bAllow )
440             bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELL_INT_BINDING );
441 
442         // then, we only offer this for list boxes
443         if ( bAllow )
444         {
445             try
446             {
447                 sal_Int16 nClassId = FormComponentType::CONTROL;
448                 m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
449                 if ( FormComponentType::LISTBOX != nClassId )
450                     bAllow = false;
451             }
452             catch( const Exception& )
453             {
454             	OSL_ENSURE( sal_False, "CellBindingHelper::isCellIntegerBindingAllowed: caught an exception!" );
455                     // are there really control models which survive isCellBindingAllowed, but don't have a ClassId
456                     // property?
457                 bAllow = false;
458             }
459         }
460 
461         return bAllow;
462     }
463 
464     //------------------------------------------------------------------------
465     bool CellBindingHelper::isCellBindingAllowed( ) const
466     {
467         bool bAllow( false );
468 
469         Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
470         if ( xBindable.is() )
471         {
472             // the control can potentially be bound to an external value
473             // Does it live within a Calc document, and is able to supply CellBindings?
474             bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELL_BINDING );
475         }
476 
477         // disallow for some types
478         // TODO: shouldn't the XBindableValue supply a list of supported types, and we can distingusih
479         // using this list? The current behavior below is somewhat hackish ...
480         if ( bAllow )
481         {
482             try
483             {
484                 sal_Int16 nClassId = FormComponentType::CONTROL;
485                 m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
486                 if ( ( FormComponentType::DATEFIELD == nClassId ) || ( FormComponentType::TIMEFIELD == nClassId ) )
487                     bAllow = false;
488             }
489             catch( const Exception& )
490             {
491             	OSL_ENSURE( sal_False, "CellBindingHelper::isCellBindingAllowed: caught an exception!" );
492                 bAllow = false;
493             }
494         }
495         return bAllow;
496     }
497 
498     //------------------------------------------------------------------------
499     bool CellBindingHelper::isCellBinding( const Reference< XValueBinding >& _rxBinding ) const
500     {
501         return doesComponentSupport( _rxBinding.get(), SERVICE_SHEET_CELL_BINDING );
502     }
503 
504     //------------------------------------------------------------------------
505     bool CellBindingHelper::isCellIntegerBinding( const Reference< XValueBinding >& _rxBinding ) const
506     {
507         return doesComponentSupport( _rxBinding.get(), SERVICE_SHEET_CELL_INT_BINDING );
508     }
509 
510     //------------------------------------------------------------------------
511     bool CellBindingHelper::isCellRangeListSource( const Reference< XListEntrySource >& _rxSource ) const
512     {
513         return doesComponentSupport( _rxSource.get(), SERVICE_SHEET_CELLRANGE_LISTSOURCE );
514     }
515 
516     //------------------------------------------------------------------------
517     bool CellBindingHelper::doesComponentSupport( const Reference< XInterface >& _rxComponent, const ::rtl::OUString& _rService ) const
518     {
519         bool bDoes = false;
520         Reference< XServiceInfo > xSI( _rxComponent, UNO_QUERY );
521         bDoes = xSI.is() && xSI->supportsService( _rService );
522         return bDoes;
523     }
524 
525     //------------------------------------------------------------------------
526     Reference< XValueBinding > CellBindingHelper::getCurrentBinding( ) const
527     {
528         Reference< XValueBinding > xBinding;
529         Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
530         if ( xBindable.is() )
531             xBinding = xBindable->getValueBinding();
532         return xBinding;
533     }
534 
535     //------------------------------------------------------------------------
536     Reference< XListEntrySource > CellBindingHelper::getCurrentListSource( ) const
537     {
538         Reference< XListEntrySource > xSource;
539         Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
540         if ( xSink.is() )
541             xSource = xSink->getListEntrySource();
542         return xSource;
543     }
544 
545     //------------------------------------------------------------------------
546     void CellBindingHelper::setBinding( const Reference< XValueBinding >& _rxBinding )
547     {
548         Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
549         OSL_PRECOND( xBindable.is(), "CellBindingHelper::setBinding: the object is not bindable!" );
550         if ( xBindable.is() )
551             xBindable->setValueBinding( _rxBinding );
552     }
553 
554     //------------------------------------------------------------------------
555     void CellBindingHelper::setListSource( const Reference< XListEntrySource >& _rxSource )
556     {
557         Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
558         OSL_PRECOND( xSink.is(), "CellBindingHelper::setListSource: the object is no list entry sink!" );
559         if ( xSink.is() )
560             xSink->setListEntrySource( _rxSource );
561     }
562 
563 //............................................................................
564 }   // namespace pcr
565 //............................................................................
566