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 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 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 81 RangeHighlighter::~RangeHighlighter() 82 {} 83 84 // ____ XRangeHighlighter ____ 85 Sequence< chart2::data::HighlightedRange > SAL_CALL RangeHighlighter::getSelectedRanges() 86 { 87 return m_aSelectedRanges; 88 } 89 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 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 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 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 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 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 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 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 ____ 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 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 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 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 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 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