1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_chart2.hxx"
26 
27 #include "RangeHighlighter.hxx"
28 #include "WeakListenerAdapter.hxx"
29 #include "ChartModelHelper.hxx"
30 #include "DataSourceHelper.hxx"
31 #include "ContainerHelper.hxx"
32 #include "macros.hxx"
33 #include "ObjectIdentifier.hxx"
34 #include "DataSeriesHelper.hxx"
35 
36 #include <com/sun/star/chart2/XDataSeries.hpp>
37 #include <com/sun/star/chart/ErrorBarStyle.hpp>
38 #include <com/sun/star/drawing/XShape.hpp>
39 
40 #define PREFERED_DEFAULT_COLOR 0x0000ff
41 
42 using namespace ::com::sun::star;
43 
44 using ::com::sun::star::uno::Reference;
45 using ::com::sun::star::uno::Sequence;
46 using ::rtl::OUString;
47 
48 namespace
49 {
50 
lcl_fillRanges(Sequence<chart2::data::HighlightedRange> & rOutRanges,Sequence<OUString> aRangeStrings,sal_Int32 nPreferredColor=PREFERED_DEFAULT_COLOR,sal_Int32 nIndex=-1)51 void lcl_fillRanges(
52     Sequence< chart2::data::HighlightedRange > & rOutRanges,
53     Sequence< OUString > aRangeStrings,
54     sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR,
55     sal_Int32 nIndex = -1 )
56 {
57     rOutRanges.realloc( aRangeStrings.getLength());
58     for( sal_Int32 i=0; i<aRangeStrings.getLength(); ++i )
59     {
60         rOutRanges[i].RangeRepresentation = aRangeStrings[i];
61         rOutRanges[i].PreferredColor = nPreferredColor;
62         rOutRanges[i].AllowMerginigWithOtherRanges = sal_False;
63         rOutRanges[i].Index = nIndex;
64     }
65 }
66 
67 } // anonymous namespace
68 
69 namespace chart
70 {
71 
RangeHighlighter(const Reference<view::XSelectionSupplier> & xSelectionSupplier)72 RangeHighlighter::RangeHighlighter(
73     const Reference< view::XSelectionSupplier > & xSelectionSupplier ) :
74         impl::RangeHighlighter_Base( m_aMutex ),
75         m_xSelectionSupplier( xSelectionSupplier ),
76         m_nAddedListenerCount( 0 ),
77         m_bIncludeHiddenCells(true)
78 {
79 }
80 
~RangeHighlighter()81 RangeHighlighter::~RangeHighlighter()
82 {}
83 
84 // ____ XRangeHighlighter ____
getSelectedRanges()85 Sequence< chart2::data::HighlightedRange > SAL_CALL RangeHighlighter::getSelectedRanges()
86     throw (uno::RuntimeException)
87 {
88     return m_aSelectedRanges;
89 }
90 
determineRanges()91 void RangeHighlighter::determineRanges()
92 {
93     m_aSelectedRanges.realloc( 0 );
94     if( m_xSelectionSupplier.is())
95     {
96         try
97         {
98             Reference< frame::XController > xController( m_xSelectionSupplier, uno::UNO_QUERY );
99             Reference< frame::XModel > xChartModel;
100             if( xController.is())
101                 xChartModel.set( xController->getModel());
102 
103             m_bIncludeHiddenCells = ChartModelHelper::isIncludeHiddenCells( xChartModel );
104 
105             uno::Any aSelection( m_xSelectionSupplier->getSelection());
106             const uno::Type& rType = aSelection.getValueType();
107 
108             if ( rType == ::getCppuType( static_cast< const OUString* >( 0 ) ) )
109             {
110                 // @todo??: maybe getSelection() should return a model object rather than a CID
111 
112                 OUString aCID;
113                 aSelection >>= aCID;
114                 if ( !aCID.isEmpty() )
115                 {
116                     ObjectType eObjectType = ObjectIdentifier::getObjectType( aCID );
117                     sal_Int32 nIndex = ObjectIdentifier::getIndexFromParticleOrCID( aCID );
118                     Reference< chart2::XDataSeries > xDataSeries( ObjectIdentifier::getDataSeriesForCID( aCID, xChartModel ) );
119                     if( OBJECTTYPE_LEGEND_ENTRY == eObjectType )
120                     {
121                         OUString aParentParticel( ObjectIdentifier::getFullParentParticle( aCID ) );
122                         ObjectType eParentObjectType = ObjectIdentifier::getObjectType( aParentParticel );
123                         eObjectType = eParentObjectType;
124                         if( OBJECTTYPE_DATA_POINT == eObjectType )
125                             nIndex = ObjectIdentifier::getIndexFromParticleOrCID( aParentParticel );
126                     }
127 
128                     if( OBJECTTYPE_DATA_POINT == eObjectType || OBJECTTYPE_DATA_LABEL == eObjectType )
129                     {
130                         // Data Point
131                         fillRangesForDataPoint( xDataSeries, nIndex );
132                         return;
133                     }
134                     else if( OBJECTTYPE_DATA_ERRORS == eObjectType )
135                     {
136                         // select error bar ranges, or data series, if the style is
137                         // not set to FROM_DATA
138                         fillRangesForErrorBars( ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ), xDataSeries );
139                         return;
140                     }
141                     else if( xDataSeries.is() )
142                     {
143                         // Data Series
144                         fillRangesForDataSeries( xDataSeries );
145                         return;
146                     }
147                     else if( OBJECTTYPE_AXIS == eObjectType )
148                     {
149                         // Axis (Categories)
150                         Reference< chart2::XAxis > xAxis( ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ), uno::UNO_QUERY );
151                         if( xAxis.is())
152                         {
153                             fillRangesForCategories( xAxis );
154                             return;
155                         }
156                     }
157                     else if( OBJECTTYPE_PAGE == eObjectType
158                              || OBJECTTYPE_DIAGRAM == eObjectType
159                              || OBJECTTYPE_DIAGRAM_WALL == eObjectType
160                              || OBJECTTYPE_DIAGRAM_FLOOR == eObjectType
161                         )
162                     {
163                         // Diagram
164                         Reference< chart2::XDiagram > xDia( ObjectIdentifier::getDiagramForCID( aCID, xChartModel ) );
165                         if( xDia.is())
166                         {
167                             fillRangesForDiagram( xDia );
168                             return;
169                         }
170                     }
171                 }
172             }
173             else if ( rType == ::getCppuType( static_cast< const Reference< drawing::XShape >* >( 0 ) ) )
174             {
175                 // #i12587# support for shapes in chart
176                 Reference< drawing::XShape > xShape;
177                 aSelection >>= xShape;
178                 if ( xShape.is() )
179                 {
180                     return;
181                 }
182             }
183             else
184             {
185                 //if nothing is selected select all ranges
186                 Reference< chart2::XChartDocument > xChartDoc( xChartModel, uno::UNO_QUERY_THROW );
187                 fillRangesForDiagram( xChartDoc->getFirstDiagram() );
188                 return;
189             }
190         }
191         catch( const uno::Exception & ex )
192         {
193             ASSERT_EXCEPTION( ex );
194         }
195     }
196 }
197 
fillRangesForDiagram(const Reference<chart2::XDiagram> & xDiagram)198 void RangeHighlighter::fillRangesForDiagram( const Reference< chart2::XDiagram > & xDiagram )
199 {
200     Sequence< OUString > aSelectedRanges( DataSourceHelper::getUsedDataRanges( xDiagram ));
201     m_aSelectedRanges.realloc( aSelectedRanges.getLength());
202     // @todo: merge ranges
203     for( sal_Int32 i=0; i<aSelectedRanges.getLength(); ++i )
204     {
205         m_aSelectedRanges[i].RangeRepresentation = aSelectedRanges[i];
206         m_aSelectedRanges[i].Index = -1;
207         m_aSelectedRanges[i].PreferredColor = PREFERED_DEFAULT_COLOR;
208         m_aSelectedRanges[i].AllowMerginigWithOtherRanges = sal_True;
209     }
210 }
211 
fillRangesForDataSeries(const uno::Reference<chart2::XDataSeries> & xSeries)212 void RangeHighlighter::fillRangesForDataSeries( const uno::Reference< chart2::XDataSeries > & xSeries )
213 {
214     sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR;
215     Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
216     if( xSource.is())
217         lcl_fillRanges( m_aSelectedRanges,
218                         ::chart::DataSourceHelper::getRangesFromDataSource( xSource ),
219                         nPreferredColor );
220 }
221 
fillRangesForErrorBars(const uno::Reference<beans::XPropertySet> & xErrorBar,const uno::Reference<chart2::XDataSeries> & xSeries)222 void RangeHighlighter::fillRangesForErrorBars(
223     const uno::Reference< beans::XPropertySet > & xErrorBar,
224     const uno::Reference< chart2::XDataSeries > & xSeries )
225 {
226     // only show error bar ranges, if the style is set to FROM_DATA
227     bool bUsesRangesAsErrorBars = false;
228     try
229     {
230         sal_Int32 nStyle = ::com::sun::star::chart::ErrorBarStyle::NONE;
231         bUsesRangesAsErrorBars =
232             ( xErrorBar.is() &&
233               (xErrorBar->getPropertyValue( C2U("ErrorBarStyle")) >>= nStyle) &&
234               nStyle == ::com::sun::star::chart::ErrorBarStyle::FROM_DATA );
235     }
236     catch( const uno::Exception & ex )
237     {
238         ASSERT_EXCEPTION( ex );
239     }
240 
241     if( bUsesRangesAsErrorBars )
242     {
243         sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR;
244         Reference< chart2::data::XDataSource > xSource( xErrorBar, uno::UNO_QUERY );
245         if( xSource.is())
246             lcl_fillRanges( m_aSelectedRanges,
247                             ::chart::DataSourceHelper::getRangesFromDataSource( xSource ),
248                             nPreferredColor );
249     }
250     else
251     {
252         fillRangesForDataSeries( xSeries );
253     }
254 }
255 
fillRangesForCategories(const Reference<chart2::XAxis> & xAxis)256 void RangeHighlighter::fillRangesForCategories( const Reference< chart2::XAxis > & xAxis )
257 {
258     if( ! xAxis.is())
259         return;
260     chart2::ScaleData aData( xAxis->getScaleData());
261     lcl_fillRanges( m_aSelectedRanges,
262                     DataSourceHelper::getRangesFromLabeledDataSequence( aData.Categories ));
263 }
264 
fillRangesForDataPoint(const Reference<uno::XInterface> & xDataSeries,sal_Int32 nIndex)265 void RangeHighlighter::fillRangesForDataPoint( const Reference< uno::XInterface > & xDataSeries, sal_Int32 nIndex )
266 {
267     sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR;
268     if( xDataSeries.is())
269     {
270         Reference< chart2::data::XDataSource > xSource( xDataSeries, uno::UNO_QUERY );
271         if( xSource.is() )
272         {
273             ::std::vector< chart2::data::HighlightedRange > aHilightedRanges;
274             Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeqSeq( xSource->getDataSequences());
275             for( sal_Int32 i=0; i<aLSeqSeq.getLength(); ++i )
276             {
277                 Reference< chart2::data::XDataSequence > xLabel( aLSeqSeq[i]->getLabel());
278                 Reference< chart2::data::XDataSequence > xValues( aLSeqSeq[i]->getValues());
279 
280                 if( xLabel.is())
281                     aHilightedRanges.push_back(
282                         chart2::data::HighlightedRange(
283                             xLabel->getSourceRangeRepresentation(),
284                             -1,
285                             nPreferredColor,
286                             sal_False ));
287 
288                 sal_Int32 nUnhiddenIndex = DataSeriesHelper::translateIndexFromHiddenToFullSequence( nIndex, xValues, !m_bIncludeHiddenCells );
289                 if( xValues.is())
290                     aHilightedRanges.push_back(
291                         chart2::data::HighlightedRange(
292                             xValues->getSourceRangeRepresentation(),
293                             nUnhiddenIndex,
294                             nPreferredColor,
295                             sal_False ));
296             }
297             m_aSelectedRanges = ContainerHelper::ContainerToSequence( aHilightedRanges );
298         }
299     }
300 }
301 
addSelectionChangeListener(const Reference<view::XSelectionChangeListener> & xListener)302 void SAL_CALL RangeHighlighter::addSelectionChangeListener( const Reference< view::XSelectionChangeListener >& xListener )
303     throw (uno::RuntimeException)
304 {
305     if(!xListener.is())
306         return;
307 
308     if( m_nAddedListenerCount == 0 )
309         startListening();
310     rBHelper.addListener( ::getCppuType( & xListener ), xListener);
311     ++m_nAddedListenerCount;
312 
313     //bring the new listener up to the current state
314     lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
315     xListener->selectionChanged( aEvent );
316 }
317 
removeSelectionChangeListener(const Reference<view::XSelectionChangeListener> & xListener)318 void SAL_CALL RangeHighlighter::removeSelectionChangeListener( const Reference< view::XSelectionChangeListener >& xListener )
319     throw (uno::RuntimeException)
320 {
321     rBHelper.removeListener( ::getCppuType( & xListener ), xListener );
322     --m_nAddedListenerCount;
323     if( m_nAddedListenerCount == 0 )
324         stopListening();
325 }
326 
327 // ____ XSelectionChangeListener ____
selectionChanged(const lang::EventObject &)328 void SAL_CALL RangeHighlighter::selectionChanged( const lang::EventObject& /*aEvent*/ )
329     throw (uno::RuntimeException)
330 {
331     determineRanges();
332 
333     // determine ranges of selected view objects
334     // if changed, fire an event
335     fireSelectionEvent();
336 }
337 
fireSelectionEvent()338 void RangeHighlighter::fireSelectionEvent()
339 {
340 	::cppu::OInterfaceContainerHelper* pIC = rBHelper.getContainer(
341         ::getCppuType((const uno::Reference< view::XSelectionChangeListener >*)0) );
342 	if( pIC )
343 	{
344 		lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
345 		::cppu::OInterfaceIteratorHelper aIt( *pIC );
346 		while( aIt.hasMoreElements() )
347         {
348             uno::Reference< view::XSelectionChangeListener > xListener( aIt.next(), uno::UNO_QUERY );
349             if( xListener.is() )
350                 xListener->selectionChanged( aEvent );
351         }
352 	}
353 }
354 
disposing(const lang::EventObject & Source)355 void SAL_CALL RangeHighlighter::disposing( const lang::EventObject& Source )
356     throw (uno::RuntimeException)
357 {
358     if( Source.Source == m_xSelectionSupplier )
359     {
360         m_xSelectionSupplier.clear();
361         m_aSelectedRanges.realloc( 0 );
362         fireSelectionEvent();
363     }
364 }
365 
startListening()366 void RangeHighlighter::startListening()
367 {
368     if( m_xSelectionSupplier.is())
369     {
370         if( ! m_xListener.is())
371         {
372             m_xListener.set( new WeakSelectionChangeListenerAdapter( this ));
373             determineRanges();
374         }
375         m_xSelectionSupplier->addSelectionChangeListener( m_xListener );
376     }
377 }
378 
stopListening()379 void RangeHighlighter::stopListening()
380 {
381     if( m_xSelectionSupplier.is() && m_xListener.is())
382     {
383         m_xSelectionSupplier->removeSelectionChangeListener( m_xListener );
384         m_xListener.clear();
385     }
386 }
387 
388 
389 // ____ WeakComponentImplHelperBase ____
390 // is called when dispose() is called at this component
disposing()391 void SAL_CALL RangeHighlighter::disposing()
392 {
393     // @todo: remove listener. Currently the controller shows an assertion
394     // because it is already disposed
395 //     stopListening();
396     m_xListener.clear();
397     m_xSelectionSupplier.clear();
398     m_nAddedListenerCount =  0;
399     m_aSelectedRanges.realloc( 0 );
400 }
401 
402 } //  namespace chart
403