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