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_charttools.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 {
87 return m_aSelectedRanges;
88 }
89
determineRanges()90 void RangeHighlighter::determineRanges()
91 {
92 m_aSelectedRanges.realloc( 0 );
93 if( m_xSelectionSupplier.is())
94 {
95 try
96 {
97 Reference< frame::XController > xController( m_xSelectionSupplier, uno::UNO_QUERY );
98 Reference< frame::XModel > xChartModel;
99 if( xController.is())
100 xChartModel.set( xController->getModel());
101
102 m_bIncludeHiddenCells = ChartModelHelper::isIncludeHiddenCells( xChartModel );
103
104 uno::Any aSelection( m_xSelectionSupplier->getSelection());
105 const uno::Type& rType = aSelection.getValueType();
106
107 if ( rType == ::getCppuType( static_cast< const OUString* >( 0 ) ) )
108 {
109 // @todo??: maybe getSelection() should return a model object rather than a CID
110
111 OUString aCID;
112 aSelection >>= aCID;
113 if ( !aCID.isEmpty() )
114 {
115 ObjectType eObjectType = ObjectIdentifier::getObjectType( aCID );
116 sal_Int32 nIndex = ObjectIdentifier::getIndexFromParticleOrCID( aCID );
117 Reference< chart2::XDataSeries > xDataSeries( ObjectIdentifier::getDataSeriesForCID( aCID, xChartModel ) );
118 if( OBJECTTYPE_LEGEND_ENTRY == eObjectType )
119 {
120 OUString aParentParticel( ObjectIdentifier::getFullParentParticle( aCID ) );
121 ObjectType eParentObjectType = ObjectIdentifier::getObjectType( aParentParticel );
122 eObjectType = eParentObjectType;
123 if( OBJECTTYPE_DATA_POINT == eObjectType )
124 nIndex = ObjectIdentifier::getIndexFromParticleOrCID( aParentParticel );
125 }
126
127 if( OBJECTTYPE_DATA_POINT == eObjectType || OBJECTTYPE_DATA_LABEL == eObjectType )
128 {
129 // Data Point
130 fillRangesForDataPoint( xDataSeries, nIndex );
131 return;
132 }
133 else if( OBJECTTYPE_DATA_ERRORS == eObjectType )
134 {
135 // select error bar ranges, or data series, if the style is
136 // not set to FROM_DATA
137 fillRangesForErrorBars( ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ), xDataSeries );
138 return;
139 }
140 else if( xDataSeries.is() )
141 {
142 // Data Series
143 fillRangesForDataSeries( xDataSeries );
144 return;
145 }
146 else if( OBJECTTYPE_AXIS == eObjectType )
147 {
148 // Axis (Categories)
149 Reference< chart2::XAxis > xAxis( ObjectIdentifier::getObjectPropertySet( aCID, xChartModel ), uno::UNO_QUERY );
150 if( xAxis.is())
151 {
152 fillRangesForCategories( xAxis );
153 return;
154 }
155 }
156 else if( OBJECTTYPE_PAGE == eObjectType
157 || OBJECTTYPE_DIAGRAM == eObjectType
158 || OBJECTTYPE_DIAGRAM_WALL == eObjectType
159 || OBJECTTYPE_DIAGRAM_FLOOR == eObjectType
160 )
161 {
162 // Diagram
163 Reference< chart2::XDiagram > xDia( ObjectIdentifier::getDiagramForCID( aCID, xChartModel ) );
164 if( xDia.is())
165 {
166 fillRangesForDiagram( xDia );
167 return;
168 }
169 }
170 }
171 }
172 else if ( rType == ::getCppuType( static_cast< const Reference< drawing::XShape >* >( 0 ) ) )
173 {
174 // #i12587# support for shapes in chart
175 Reference< drawing::XShape > xShape;
176 aSelection >>= xShape;
177 if ( xShape.is() )
178 {
179 return;
180 }
181 }
182 else
183 {
184 //if nothing is selected select all ranges
185 Reference< chart2::XChartDocument > xChartDoc( xChartModel, uno::UNO_QUERY_THROW );
186 fillRangesForDiagram( xChartDoc->getFirstDiagram() );
187 return;
188 }
189 }
190 catch( const uno::Exception & ex )
191 {
192 ASSERT_EXCEPTION( ex );
193 }
194 }
195 }
196
fillRangesForDiagram(const Reference<chart2::XDiagram> & xDiagram)197 void RangeHighlighter::fillRangesForDiagram( const Reference< chart2::XDiagram > & xDiagram )
198 {
199 Sequence< OUString > aSelectedRanges( DataSourceHelper::getUsedDataRanges( xDiagram ));
200 m_aSelectedRanges.realloc( aSelectedRanges.getLength());
201 // @todo: merge ranges
202 for( sal_Int32 i=0; i<aSelectedRanges.getLength(); ++i )
203 {
204 m_aSelectedRanges[i].RangeRepresentation = aSelectedRanges[i];
205 m_aSelectedRanges[i].Index = -1;
206 m_aSelectedRanges[i].PreferredColor = PREFERED_DEFAULT_COLOR;
207 m_aSelectedRanges[i].AllowMerginigWithOtherRanges = sal_True;
208 }
209 }
210
fillRangesForDataSeries(const uno::Reference<chart2::XDataSeries> & xSeries)211 void RangeHighlighter::fillRangesForDataSeries( const uno::Reference< chart2::XDataSeries > & xSeries )
212 {
213 sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR;
214 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
215 if( xSource.is())
216 lcl_fillRanges( m_aSelectedRanges,
217 ::chart::DataSourceHelper::getRangesFromDataSource( xSource ),
218 nPreferredColor );
219 }
220
fillRangesForErrorBars(const uno::Reference<beans::XPropertySet> & xErrorBar,const uno::Reference<chart2::XDataSeries> & xSeries)221 void RangeHighlighter::fillRangesForErrorBars(
222 const uno::Reference< beans::XPropertySet > & xErrorBar,
223 const uno::Reference< chart2::XDataSeries > & xSeries )
224 {
225 // only show error bar ranges, if the style is set to FROM_DATA
226 bool bUsesRangesAsErrorBars = false;
227 try
228 {
229 sal_Int32 nStyle = ::com::sun::star::chart::ErrorBarStyle::NONE;
230 bUsesRangesAsErrorBars =
231 ( xErrorBar.is() &&
232 (xErrorBar->getPropertyValue( C2U("ErrorBarStyle")) >>= nStyle) &&
233 nStyle == ::com::sun::star::chart::ErrorBarStyle::FROM_DATA );
234 }
235 catch( const uno::Exception & ex )
236 {
237 ASSERT_EXCEPTION( ex );
238 }
239
240 if( bUsesRangesAsErrorBars )
241 {
242 sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR;
243 Reference< chart2::data::XDataSource > xSource( xErrorBar, uno::UNO_QUERY );
244 if( xSource.is())
245 lcl_fillRanges( m_aSelectedRanges,
246 ::chart::DataSourceHelper::getRangesFromDataSource( xSource ),
247 nPreferredColor );
248 }
249 else
250 {
251 fillRangesForDataSeries( xSeries );
252 }
253 }
254
fillRangesForCategories(const Reference<chart2::XAxis> & xAxis)255 void RangeHighlighter::fillRangesForCategories( const Reference< chart2::XAxis > & xAxis )
256 {
257 if( ! xAxis.is())
258 return;
259 chart2::ScaleData aData( xAxis->getScaleData());
260 lcl_fillRanges( m_aSelectedRanges,
261 DataSourceHelper::getRangesFromLabeledDataSequence( aData.Categories ));
262 }
263
fillRangesForDataPoint(const Reference<uno::XInterface> & xDataSeries,sal_Int32 nIndex)264 void RangeHighlighter::fillRangesForDataPoint( const Reference< uno::XInterface > & xDataSeries, sal_Int32 nIndex )
265 {
266 sal_Int32 nPreferredColor = PREFERED_DEFAULT_COLOR;
267 if( xDataSeries.is())
268 {
269 Reference< chart2::data::XDataSource > xSource( xDataSeries, uno::UNO_QUERY );
270 if( xSource.is() )
271 {
272 ::std::vector< chart2::data::HighlightedRange > aHilightedRanges;
273 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeqSeq( xSource->getDataSequences());
274 for( sal_Int32 i=0; i<aLSeqSeq.getLength(); ++i )
275 {
276 Reference< chart2::data::XDataSequence > xLabel( aLSeqSeq[i]->getLabel());
277 Reference< chart2::data::XDataSequence > xValues( aLSeqSeq[i]->getValues());
278
279 if( xLabel.is())
280 aHilightedRanges.push_back(
281 chart2::data::HighlightedRange(
282 xLabel->getSourceRangeRepresentation(),
283 -1,
284 nPreferredColor,
285 sal_False ));
286
287 sal_Int32 nUnhiddenIndex = DataSeriesHelper::translateIndexFromHiddenToFullSequence( nIndex, xValues, !m_bIncludeHiddenCells );
288 if( xValues.is())
289 aHilightedRanges.push_back(
290 chart2::data::HighlightedRange(
291 xValues->getSourceRangeRepresentation(),
292 nUnhiddenIndex,
293 nPreferredColor,
294 sal_False ));
295 }
296 m_aSelectedRanges = ContainerHelper::ContainerToSequence( aHilightedRanges );
297 }
298 }
299 }
300
addSelectionChangeListener(const Reference<view::XSelectionChangeListener> & xListener)301 void SAL_CALL RangeHighlighter::addSelectionChangeListener( const Reference< view::XSelectionChangeListener >& xListener )
302 {
303 if(!xListener.is())
304 return;
305
306 if( m_nAddedListenerCount == 0 )
307 startListening();
308 rBHelper.addListener( ::getCppuType( & xListener ), xListener);
309 ++m_nAddedListenerCount;
310
311 //bring the new listener up to the current state
312 lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
313 xListener->selectionChanged( aEvent );
314 }
315
removeSelectionChangeListener(const Reference<view::XSelectionChangeListener> & xListener)316 void SAL_CALL RangeHighlighter::removeSelectionChangeListener( const Reference< view::XSelectionChangeListener >& xListener )
317 {
318 rBHelper.removeListener( ::getCppuType( & xListener ), xListener );
319 --m_nAddedListenerCount;
320 if( m_nAddedListenerCount == 0 )
321 stopListening();
322 }
323
324 // ____ XSelectionChangeListener ____
selectionChanged(const lang::EventObject &)325 void SAL_CALL RangeHighlighter::selectionChanged( const lang::EventObject& /*aEvent*/ )
326 {
327 determineRanges();
328
329 // determine ranges of selected view objects
330 // if changed, fire an event
331 fireSelectionEvent();
332 }
333
fireSelectionEvent()334 void RangeHighlighter::fireSelectionEvent()
335 {
336 ::cppu::OInterfaceContainerHelper* pIC = rBHelper.getContainer(
337 ::getCppuType((const uno::Reference< view::XSelectionChangeListener >*)0) );
338 if( pIC )
339 {
340 lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
341 ::cppu::OInterfaceIteratorHelper aIt( *pIC );
342 while( aIt.hasMoreElements() )
343 {
344 uno::Reference< view::XSelectionChangeListener > xListener( aIt.next(), uno::UNO_QUERY );
345 if( xListener.is() )
346 xListener->selectionChanged( aEvent );
347 }
348 }
349 }
350
disposing(const lang::EventObject & Source)351 void SAL_CALL RangeHighlighter::disposing( const lang::EventObject& Source )
352 {
353 if( Source.Source == m_xSelectionSupplier )
354 {
355 m_xSelectionSupplier.clear();
356 m_aSelectedRanges.realloc( 0 );
357 fireSelectionEvent();
358 }
359 }
360
startListening()361 void RangeHighlighter::startListening()
362 {
363 if( m_xSelectionSupplier.is())
364 {
365 if( ! m_xListener.is())
366 {
367 m_xListener.set( new WeakSelectionChangeListenerAdapter( this ));
368 determineRanges();
369 }
370 m_xSelectionSupplier->addSelectionChangeListener( m_xListener );
371 }
372 }
373
stopListening()374 void RangeHighlighter::stopListening()
375 {
376 if( m_xSelectionSupplier.is() && m_xListener.is())
377 {
378 m_xSelectionSupplier->removeSelectionChangeListener( m_xListener );
379 m_xListener.clear();
380 }
381 }
382
383
384 // ____ WeakComponentImplHelperBase ____
385 // is called when dispose() is called at this component
disposing()386 void SAL_CALL RangeHighlighter::disposing()
387 {
388 // @todo: remove listener. Currently the controller shows an assertion
389 // because it is already disposed
390 // stopListening();
391 m_xListener.clear();
392 m_xSelectionSupplier.clear();
393 m_nAddedListenerCount = 0;
394 m_aSelectedRanges.realloc( 0 );
395 }
396
397 } // namespace chart
398