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 "DataBrowserModel.hxx" 28 #include "DialogModel.hxx" 29 #include "ChartModelHelper.hxx" 30 #include "DiagramHelper.hxx" 31 #include "DataSeriesHelper.hxx" 32 #include "PropertyHelper.hxx" 33 #include "ControllerLockGuard.hxx" 34 #include "macros.hxx" 35 #include "StatisticsHelper.hxx" 36 #include "ContainerHelper.hxx" 37 #include "ChartTypeHelper.hxx" 38 #include "chartview/ExplicitValueProvider.hxx" 39 #include "ExplicitCategoriesProvider.hxx" 40 41 #include <com/sun/star/container/XIndexReplace.hpp> 42 #include <com/sun/star/chart2/XAxis.hpp> 43 #include <com/sun/star/chart2/XDataSeriesContainer.hpp> 44 #include <com/sun/star/chart2/XInternalDataProvider.hpp> 45 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> 46 #include <com/sun/star/chart2/XChartTypeContainer.hpp> 47 #include <com/sun/star/chart2/data/XDataSource.hpp> 48 #include <com/sun/star/chart2/data/XDataSink.hpp> 49 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp> 50 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp> 51 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp> 52 #include <com/sun/star/util/XModifiable.hpp> 53 54 #include <rtl/math.hxx> 55 56 #include <algorithm> 57 58 #if OSL_DEBUG_LEVEL > 1 59 #include <cstdio> 60 #endif 61 62 using namespace ::com::sun::star; 63 64 using ::com::sun::star::uno::Reference; 65 using ::com::sun::star::uno::Sequence; 66 using ::rtl::OUString; 67 68 namespace 69 { 70 OUString lcl_getRole( 71 const Reference< chart2::data::XDataSequence > & xSeq ) 72 { 73 OUString aResult; 74 Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY ); 75 if( xProp.is()) 76 { 77 try 78 { 79 xProp->getPropertyValue( C2U("Role")) >>= aResult; 80 } 81 catch( const uno::Exception & ex ) 82 { 83 ASSERT_EXCEPTION( ex ); 84 } 85 } 86 return aResult; 87 } 88 89 90 OUString lcl_getRole( 91 const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) 92 { 93 OUString aResult; 94 if( xLSeq.is()) 95 aResult = lcl_getRole( xLSeq->getValues()); 96 return aResult; 97 } 98 99 OUString lcl_getUIRoleName( 100 const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) 101 { 102 OUString aResult( lcl_getRole( xLSeq )); 103 if( !aResult.isEmpty() ) 104 aResult = ::chart::DialogModel::ConvertRoleFromInternalToUI( aResult ); 105 return aResult; 106 } 107 108 void lcl_copyDataSequenceProperties( 109 const Reference< chart2::data::XDataSequence > & xOldSequence, 110 const Reference< chart2::data::XDataSequence > & xNewSequence ) 111 { 112 Reference< beans::XPropertySet > xOldSeqProp( xOldSequence, uno::UNO_QUERY ); 113 Reference< beans::XPropertySet > xNewSeqProp( xNewSequence, uno::UNO_QUERY ); 114 comphelper::copyProperties( xOldSeqProp, xNewSeqProp ); 115 } 116 117 bool lcl_SequenceOfSeriesIsShared( 118 const Reference< chart2::XDataSeries > & xSeries, 119 const Reference< chart2::data::XDataSequence > & xValues ) 120 { 121 bool bResult = false; 122 if( !xValues.is()) 123 return bResult; 124 try 125 { 126 OUString aValuesRole( lcl_getRole( xValues )); 127 OUString aValuesRep( xValues->getSourceRangeRepresentation()); 128 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY_THROW ); 129 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences()); 130 for( sal_Int32 i=0; i<aLSeq.getLength(); ++i ) 131 if( aLSeq[i].is() && 132 lcl_getRole( aLSeq[i] ).equals( aValuesRole )) 133 { 134 // getValues().is(), because lcl_getRole checked that already 135 bResult = (aValuesRep == aLSeq[i]->getValues()->getSourceRangeRepresentation()); 136 // assumption: a role appears only once in a series 137 break; 138 } 139 } 140 catch( const uno::Exception & ex ) 141 { 142 ASSERT_EXCEPTION( ex ); 143 } 144 return bResult; 145 } 146 147 typedef ::std::vector< Reference< chart2::data::XLabeledDataSequence > > lcl_tSharedSeqVec; 148 149 lcl_tSharedSeqVec lcl_getSharedSequences( const Sequence< Reference< chart2::XDataSeries > > & rSeries ) 150 { 151 // @todo: if only some series share a sequence, those have to be duplicated 152 // and made unshared for all series 153 lcl_tSharedSeqVec aResult; 154 // if we have only one series, we don't want any shared sequences 155 if( rSeries.getLength() <= 1 ) 156 return aResult; 157 158 Reference< chart2::data::XDataSource > xSource( rSeries[0], uno::UNO_QUERY ); 159 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences()); 160 for( sal_Int32 nIdx=0; nIdx<aLSeq.getLength(); ++nIdx ) 161 { 162 Reference< chart2::data::XDataSequence > xValues( aLSeq[nIdx]->getValues()); 163 bool bShared = true; 164 for( sal_Int32 nSeriesIdx=1; nSeriesIdx<rSeries.getLength(); ++nSeriesIdx ) 165 { 166 bShared = lcl_SequenceOfSeriesIsShared( rSeries[nSeriesIdx], xValues ); 167 if( !bShared ) 168 break; 169 } 170 if( bShared ) 171 aResult.push_back( aLSeq[nIdx] ); 172 } 173 174 return aResult; 175 } 176 177 sal_Int32 lcl_getValuesRepresentationIndex( 178 const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) 179 { 180 sal_Int32 nResult = -1; 181 if( xLSeq.is()) 182 { 183 Reference< chart2::data::XDataSequence > xSeq( xLSeq->getValues()); 184 if( xSeq.is()) 185 { 186 OUString aRep( xSeq->getSourceRangeRepresentation()); 187 nResult = aRep.toInt32(); 188 } 189 } 190 return nResult; 191 } 192 193 struct lcl_RepresentationsOfLSeqMatch : public ::std::unary_function< Reference< chart2::data::XLabeledDataSequence >, bool > 194 { 195 lcl_RepresentationsOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) : 196 m_aValuesRep( xLSeq.is() ? 197 (xLSeq->getValues().is() ? xLSeq->getValues()->getSourceRangeRepresentation() : OUString()) 198 : OUString() ) 199 {} 200 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) 201 { 202 return (xLSeq.is() && 203 xLSeq->getValues().is() && 204 (xLSeq->getValues()->getSourceRangeRepresentation() == m_aValuesRep )); 205 } 206 private: 207 OUString m_aValuesRep; 208 }; 209 210 struct lcl_RolesOfLSeqMatch : public ::std::unary_function< Reference< chart2::data::XLabeledDataSequence >, bool > 211 { 212 lcl_RolesOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) : 213 m_aRole( lcl_getRole( xLSeq )) 214 {} 215 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) 216 { 217 return lcl_getRole( xLSeq ).equals( m_aRole ); 218 } 219 private: 220 OUString m_aRole; 221 }; 222 223 bool lcl_ShowCategories( const Reference< chart2::XDiagram > & /* xDiagram */ ) 224 { 225 // show categories for all charts 226 return true; 227 // return DiagramHelper::isCategoryDiagram( xDiagram ); 228 } 229 230 bool lcl_ShowCategoriesAsDataLabel( const Reference< chart2::XDiagram > & xDiagram ) 231 { 232 return ! ::chart::DiagramHelper::isCategoryDiagram( xDiagram ); 233 } 234 235 } // anonymous namespace 236 237 namespace chart 238 { 239 240 241 struct DataBrowserModel::tDataColumn 242 { 243 ::com::sun::star::uno::Reference< 244 ::com::sun::star::chart2::XDataSeries > m_xDataSeries; 245 sal_Int32 m_nIndexInDataSeries; 246 ::rtl::OUString m_aUIRoleName; 247 ::com::sun::star::uno::Reference< 248 ::com::sun::star::chart2::data::XLabeledDataSequence > m_xLabeledDataSequence; 249 eCellType m_eCellType; 250 sal_Int32 m_nNumberFormatKey; 251 252 // default CTOR 253 tDataColumn() : m_nIndexInDataSeries( -1 ), m_eCellType( TEXT ), m_nNumberFormatKey( 0 ) {} 254 // "full" CTOR 255 tDataColumn( 256 const ::com::sun::star::uno::Reference< 257 ::com::sun::star::chart2::XDataSeries > & xDataSeries, 258 sal_Int32 nIndexInDataSeries, 259 ::rtl::OUString aUIRoleName, 260 ::com::sun::star::uno::Reference< 261 ::com::sun::star::chart2::data::XLabeledDataSequence > xLabeledDataSequence, 262 eCellType aCellType, 263 sal_Int32 nNumberFormatKey ) : 264 m_xDataSeries( xDataSeries ), 265 m_nIndexInDataSeries( nIndexInDataSeries ), 266 m_aUIRoleName( aUIRoleName ), 267 m_xLabeledDataSequence( xLabeledDataSequence ), 268 m_eCellType( aCellType ), 269 m_nNumberFormatKey( nNumberFormatKey ) 270 {} 271 }; 272 273 struct DataBrowserModel::implColumnLess : public ::std::binary_function< 274 DataBrowserModel::tDataColumn, DataBrowserModel::tDataColumn, bool > 275 { 276 bool operator() ( const first_argument_type & rLeft, const second_argument_type & rRight ) 277 { 278 if( rLeft.m_xLabeledDataSequence.is() && rRight.m_xLabeledDataSequence.is()) 279 { 280 return DialogModel::GetRoleIndexForSorting( lcl_getRole( rLeft.m_xLabeledDataSequence )) < 281 DialogModel::GetRoleIndexForSorting( lcl_getRole( rRight.m_xLabeledDataSequence )); 282 } 283 return true; 284 } 285 }; 286 287 DataBrowserModel::DataBrowserModel( 288 const Reference< chart2::XChartDocument > & xChartDoc, 289 const Reference< uno::XComponentContext > & xContext ) : 290 m_xChartDocument( xChartDoc ), 291 m_xContext( xContext ), 292 m_apDialogModel( new DialogModel( xChartDoc, xContext )) 293 { 294 updateFromModel(); 295 } 296 297 DataBrowserModel::~DataBrowserModel() 298 {} 299 300 namespace 301 { 302 struct lcl_DataSeriesOfHeaderMatches : public ::std::unary_function< ::chart::DataBrowserModel::tDataHeader, bool > 303 { 304 lcl_DataSeriesOfHeaderMatches( 305 const Reference< chart2::XDataSeries > & xSeriesToCompareWith ) : 306 m_xSeries( xSeriesToCompareWith ) 307 {} 308 bool operator() ( const ::chart::DataBrowserModel::tDataHeader & rHeader ) 309 { 310 return (m_xSeries == rHeader.m_xDataSeries); 311 } 312 private: 313 Reference< chart2::XDataSeries > m_xSeries; 314 }; 315 } 316 317 void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex ) 318 { 319 OSL_ASSERT( m_apDialogModel.get()); 320 Reference< chart2::XInternalDataProvider > xDataProvider( 321 m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 322 if( xDataProvider.is()) 323 { 324 if( isCategoriesColumn(nAfterColumnIndex) ) 325 nAfterColumnIndex = getCategoryColumnCount()-1; 326 327 sal_Int32 nStartCol = 0; 328 Reference< chart2::XDiagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDocument )); 329 Reference< chart2::XChartType > xChartType; 330 Reference< chart2::XDataSeries > xSeries; 331 if( static_cast< tDataColumnVector::size_type >( nAfterColumnIndex ) <= m_aColumns.size()) 332 xSeries.set( m_aColumns[nAfterColumnIndex].m_xDataSeries ); 333 334 sal_Int32 nSeriesNumberFormat = 0; 335 if( xSeries.is()) 336 { 337 xChartType.set( DiagramHelper::getChartTypeOfSeries( xDiagram, xSeries )); 338 tDataHeaderVector::const_iterator aIt( 339 ::std::find_if( m_aHeaders.begin(), m_aHeaders.end(), 340 lcl_DataSeriesOfHeaderMatches( xSeries ))); 341 if( aIt != m_aHeaders.end()) 342 nStartCol = aIt->m_nEndColumn; 343 344 Reference< beans::XPropertySet > xSeriesProps( xSeries, uno::UNO_QUERY ); 345 if( xSeriesProps.is() ) 346 xSeriesProps->getPropertyValue( C2U( "NumberFormat" )) >>= nSeriesNumberFormat; 347 } 348 else 349 { 350 xChartType.set( DiagramHelper::getChartTypeByIndex( xDiagram, 0 )); 351 nStartCol = nAfterColumnIndex; 352 } 353 354 if( xChartType.is()) 355 { 356 sal_Int32 nOffset = 0; 357 if( xDiagram.is() && lcl_ShowCategories( xDiagram )) 358 nOffset=getCategoryColumnCount(); 359 // get shared sequences of current series 360 Reference< chart2::XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY ); 361 lcl_tSharedSeqVec aSharedSequences; 362 if( xSeriesCnt.is()) 363 aSharedSequences = lcl_getSharedSequences( xSeriesCnt->getDataSeries()); 364 Reference< chart2::XDataSeries > xNewSeries( 365 m_apDialogModel->insertSeriesAfter( xSeries, xChartType, true /* bCreateDataCachedSequences */ )); 366 if( xNewSeries.is()) 367 { 368 { 369 Reference< chart2::data::XDataSource > xSource( xNewSeries, uno::UNO_QUERY ); 370 if( xSource.is()) 371 { 372 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSequences( 373 xSource->getDataSequences()); 374 sal_Int32 nSeqIdx = 0; 375 sal_Int32 nSeqSize = aLSequences.getLength(); 376 nStartCol -= (nOffset - 1); 377 for( sal_Int32 nIndex = nStartCol; 378 (nSeqIdx < nSeqSize); 379 ++nSeqIdx ) 380 { 381 lcl_tSharedSeqVec::const_iterator aSharedIt( 382 ::std::find_if( aSharedSequences.begin(), aSharedSequences.end(), 383 lcl_RolesOfLSeqMatch( aLSequences[nSeqIdx] ))); 384 if( aSharedIt != aSharedSequences.end()) 385 { 386 aLSequences[nSeqIdx]->setValues( (*aSharedIt)->getValues()); 387 aLSequences[nSeqIdx]->setLabel( (*aSharedIt)->getLabel()); 388 } 389 else 390 { 391 xDataProvider->insertSequence( nIndex - 1 ); 392 393 // values 394 Reference< chart2::data::XDataSequence > xNewSeq( 395 xDataProvider->createDataSequenceByRangeRepresentation( 396 OUString::valueOf( nIndex ))); 397 lcl_copyDataSequenceProperties( 398 aLSequences[nSeqIdx]->getValues(), xNewSeq ); 399 aLSequences[nSeqIdx]->setValues( xNewSeq ); 400 401 // labels 402 Reference< chart2::data::XDataSequence > xNewLabelSeq( 403 xDataProvider->createDataSequenceByRangeRepresentation( 404 OUString( RTL_CONSTASCII_USTRINGPARAM( "label " )) + 405 OUString::valueOf( nIndex ))); 406 lcl_copyDataSequenceProperties( 407 aLSequences[nSeqIdx]->getLabel(), xNewLabelSeq ); 408 aLSequences[nSeqIdx]->setLabel( xNewLabelSeq ); 409 ++nIndex; 410 } 411 } 412 } 413 } 414 if( nSeriesNumberFormat != 0 ) 415 { 416 //give the new series the same number format as the former series especially for bubble charts thus the bubble size values can be edited with same format immidiately 417 Reference< beans::XPropertySet > xNewSeriesProps( xNewSeries, uno::UNO_QUERY ); 418 if( xNewSeriesProps.is() ) 419 xNewSeriesProps->setPropertyValue( C2U( "NumberFormat" ), uno::makeAny( nSeriesNumberFormat ) ); 420 } 421 422 updateFromModel(); 423 } 424 } 425 } 426 } 427 428 void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex ) 429 { 430 //create a new text column for complex categories 431 432 OSL_ASSERT( m_apDialogModel.get()); 433 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 434 if( xDataProvider.is() ) 435 { 436 if( !isCategoriesColumn(nAfterColumnIndex) ) 437 nAfterColumnIndex = getCategoryColumnCount()-1; 438 439 if(nAfterColumnIndex<0) 440 { 441 OSL_ENSURE( false, "wrong index for category level insertion" ); 442 return; 443 } 444 445 m_apDialogModel->startControllerLockTimer(); 446 ControllerLockGuard aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) ); 447 xDataProvider->insertComplexCategoryLevel( nAfterColumnIndex+1 ); 448 updateFromModel(); 449 } 450 } 451 452 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex ) 453 { 454 OSL_ASSERT( m_apDialogModel.get()); 455 if( static_cast< tDataColumnVector::size_type >( nAtColumnIndex ) < m_aColumns.size()) 456 { 457 Reference< chart2::XDataSeries > xSeries( m_aColumns[nAtColumnIndex].m_xDataSeries ); 458 if( xSeries.is()) 459 { 460 m_apDialogModel->deleteSeries( 461 xSeries, getHeaderForSeries( xSeries ).m_xChartType ); 462 463 //delete sequences from internal data provider that are not used anymore 464 //but do not delete sequences that are still in use by the remaining series 465 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 466 Reference< chart2::data::XDataSource > xSourceOfDeletedSeries( xSeries, uno::UNO_QUERY ); 467 if( xDataProvider.is() && xSourceOfDeletedSeries.is()) 468 { 469 ::std::vector< sal_Int32 > aSequenceIndexesToDelete; 470 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequencesOfDeletedSeries( xSourceOfDeletedSeries->getDataSequences() ); 471 Reference< chart2::XDataSeriesContainer > xSeriesCnt( getHeaderForSeries( xSeries ).m_xChartType, uno::UNO_QUERY ); 472 if( xSeriesCnt.is()) 473 { 474 Reference< chart2::data::XDataSource > xRemainingDataSource( DataSeriesHelper::getDataSource( xSeriesCnt->getDataSeries() ) ); 475 if( xRemainingDataSource.is() ) 476 { 477 ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aRemainingSeq( ContainerHelper::SequenceToVector( xRemainingDataSource->getDataSequences() ) ); 478 for( sal_Int32 i=0; i<aSequencesOfDeletedSeries.getLength(); ++i ) 479 { 480 ::std::vector< Reference< chart2::data::XLabeledDataSequence > >::const_iterator aHitIt( 481 ::std::find_if( aRemainingSeq.begin(), aRemainingSeq.end(), 482 lcl_RepresentationsOfLSeqMatch( aSequencesOfDeletedSeries[i] ))); 483 // if not used by the remaining series this sequence can be deleted 484 if( aHitIt == aRemainingSeq.end() ) 485 aSequenceIndexesToDelete.push_back( lcl_getValuesRepresentationIndex( aSequencesOfDeletedSeries[i] ) ); 486 } 487 } 488 } 489 490 // delete unnecessary sequences of the internal data 491 // iterate using greatest index first, so that deletion does not 492 // shift other sequences that will be deleted later 493 ::std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end()); 494 for( ::std::vector< sal_Int32 >::reverse_iterator aIt( 495 aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt ) 496 { 497 if( *aIt != -1 ) 498 xDataProvider->deleteSequence( *aIt ); 499 } 500 } 501 updateFromModel(); 502 } 503 else 504 { 505 //delete a category column if there is more than one level (in case of a single column we do not get here) 506 OSL_ENSURE(nAtColumnIndex>0, "wrong index for categories deletion" ); 507 508 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 509 if( xDataProvider.is() ) 510 { 511 m_apDialogModel->startControllerLockTimer(); 512 ControllerLockGuard aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) ); 513 xDataProvider->deleteComplexCategoryLevel( nAtColumnIndex ); 514 updateFromModel(); 515 } 516 } 517 } 518 } 519 520 void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex ) 521 { 522 OSL_ASSERT( m_apDialogModel.get()); 523 if( static_cast< tDataColumnVector::size_type >( nFirstColumnIndex ) < m_aColumns.size() - 1 ) 524 { 525 Reference< chart2::XDataSeries > xSeries( m_aColumns[nFirstColumnIndex].m_xDataSeries ); 526 if( xSeries.is()) 527 { 528 m_apDialogModel->moveSeries( xSeries, DialogModel::MOVE_DOWN ); 529 updateFromModel(); 530 } 531 } 532 } 533 534 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex ) 535 { 536 OSL_ASSERT( m_apDialogModel.get()); 537 Reference< chart2::XInternalDataProvider > xDataProvider( 538 m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 539 // lockControllers 540 ControllerLockGuard aGuard( m_apDialogModel->getChartModel()); 541 if( xDataProvider.is()) 542 xDataProvider->swapDataPointWithNextOneForAllSequences( nFirstIndex ); 543 // unlockControllers 544 } 545 546 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex ) 547 { 548 Reference< chart2::XInternalDataProvider > xDataProvider( 549 m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 550 // lockControllers 551 ControllerLockGuard aGuard( m_apDialogModel->getChartModel()); 552 if( xDataProvider.is()) 553 xDataProvider->insertDataPointForAllSequences( nAfterIndex ); 554 // unlockControllers 555 } 556 557 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex ) 558 { 559 Reference< chart2::XInternalDataProvider > xDataProvider( 560 m_apDialogModel->getDataProvider(), uno::UNO_QUERY ); 561 // lockControllers 562 ControllerLockGuard aGuard( m_apDialogModel->getChartModel()); 563 if( xDataProvider.is()) 564 xDataProvider->deleteDataPointForAllSequences( nAtIndex ); 565 // unlockControllers 566 } 567 568 DataBrowserModel::tDataHeader DataBrowserModel::getHeaderForSeries( 569 const Reference< chart2::XDataSeries > & xSeries ) const 570 { 571 for( tDataHeaderVector::const_iterator aIt( m_aHeaders.begin()); 572 aIt != m_aHeaders.end(); ++aIt ) 573 { 574 if( aIt->m_xDataSeries == xSeries ) 575 return (*aIt); 576 } 577 return tDataHeader(); 578 } 579 580 Reference< chart2::XDataSeries > 581 DataBrowserModel::getDataSeriesByColumn( sal_Int32 nColumn ) const 582 { 583 tDataColumnVector::size_type nIndex( nColumn ); 584 if( nIndex < m_aColumns.size()) 585 return m_aColumns[nIndex].m_xDataSeries; 586 return 0; 587 } 588 589 DataBrowserModel::eCellType DataBrowserModel::getCellType( sal_Int32 nAtColumn, sal_Int32 /* nAtRow */ ) const 590 { 591 eCellType eResult = TEXT; 592 tDataColumnVector::size_type nIndex( nAtColumn ); 593 if( nIndex < m_aColumns.size()) 594 eResult = m_aColumns[nIndex].m_eCellType; 595 return eResult; 596 } 597 598 double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow ) 599 { 600 double fResult; 601 ::rtl::math::setNan( & fResult ); 602 603 tDataColumnVector::size_type nIndex( nAtColumn ); 604 if( nIndex < m_aColumns.size() && 605 m_aColumns[ nIndex ].m_xLabeledDataSequence.is()) 606 { 607 Reference< chart2::data::XNumericalDataSequence > xData( 608 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY ); 609 if( xData.is()) 610 { 611 Sequence< double > aValues( xData->getNumericalData()); 612 if( nAtRow < aValues.getLength()) 613 fResult = aValues[nAtRow]; 614 } 615 } 616 return fResult; 617 } 618 619 uno::Any DataBrowserModel::getCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow ) 620 { 621 uno::Any aResult; 622 623 tDataColumnVector::size_type nIndex( nAtColumn ); 624 if( nIndex < m_aColumns.size() && 625 m_aColumns[ nIndex ].m_xLabeledDataSequence.is()) 626 { 627 Reference< chart2::data::XDataSequence > xData( 628 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues() ); 629 if( xData.is() ) 630 { 631 Sequence< uno::Any > aValues( xData->getData()); 632 if( nAtRow < aValues.getLength()) 633 aResult = aValues[nAtRow]; 634 } 635 } 636 return aResult; 637 } 638 639 OUString DataBrowserModel::getCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow ) 640 { 641 OUString aResult; 642 643 tDataColumnVector::size_type nIndex( nAtColumn ); 644 if( nIndex < m_aColumns.size() && 645 m_aColumns[ nIndex ].m_xLabeledDataSequence.is()) 646 { 647 Reference< chart2::data::XTextualDataSequence > xData( 648 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY ); 649 if( xData.is()) 650 { 651 Sequence< OUString > aValues( xData->getTextualData()); 652 if( nAtRow < aValues.getLength()) 653 aResult = aValues[nAtRow]; 654 } 655 } 656 return aResult; 657 } 658 659 sal_uInt32 DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn, sal_Int32 /* nAtRow */ ) 660 { 661 tDataColumnVector::size_type nIndex( nAtColumn ); 662 if( nIndex < m_aColumns.size()) 663 return m_aColumns[ nIndex ].m_nNumberFormatKey; 664 return 0; 665 } 666 667 bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow, const uno::Any & rValue ) 668 { 669 bool bResult = false; 670 tDataColumnVector::size_type nIndex( nAtColumn ); 671 if( nIndex < m_aColumns.size() && 672 m_aColumns[ nIndex ].m_xLabeledDataSequence.is()) 673 { 674 bResult = true; 675 try 676 { 677 ControllerLockGuard aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) ); 678 679 // label 680 if( nAtRow == -1 ) 681 { 682 Reference< container::XIndexReplace > xIndexReplace( 683 m_aColumns[ nIndex ].m_xLabeledDataSequence->getLabel(), uno::UNO_QUERY_THROW ); 684 xIndexReplace->replaceByIndex( 0, rValue ); 685 } 686 else 687 { 688 Reference< container::XIndexReplace > xIndexReplace( 689 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY_THROW ); 690 xIndexReplace->replaceByIndex( nAtRow, rValue ); 691 } 692 693 m_apDialogModel->startControllerLockTimer(); 694 //notify change directly to the model (this is necessary here as sequences for complex categories not known directly to the chart model so they do not notify their changes) (for complex categories see issue #i82971#) 695 Reference< util::XModifiable > xModifiable( m_xChartDocument, uno::UNO_QUERY ); 696 if( xModifiable.is() ) 697 xModifiable->setModified(true); 698 } 699 catch( const uno::Exception & ex ) 700 { 701 (void)(ex); 702 bResult = false; 703 } 704 } 705 return bResult; 706 } 707 708 bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue ) 709 { 710 return (getCellType( nAtColumn, nAtRow ) == NUMBER) && 711 setCellAny( nAtColumn, nAtRow, uno::makeAny( fValue )); 712 } 713 714 bool DataBrowserModel::setCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow, const ::rtl::OUString & rText ) 715 { 716 return (getCellType( nAtColumn, nAtRow ) == TEXT) && 717 setCellAny( nAtColumn, nAtRow, uno::makeAny( rText )); 718 } 719 720 sal_Int32 DataBrowserModel::getColumnCount() const 721 { 722 return static_cast< sal_Int32 >( m_aColumns.size()); 723 } 724 725 sal_Int32 DataBrowserModel::getMaxRowCount() const 726 { 727 sal_Int32 nResult = 0; 728 tDataColumnVector::const_iterator aIt( m_aColumns.begin()); 729 for( ; aIt != m_aColumns.end(); ++aIt ) 730 { 731 if( aIt->m_xLabeledDataSequence.is()) 732 { 733 Reference< chart2::data::XDataSequence > xSeq( 734 aIt->m_xLabeledDataSequence->getValues()); 735 if( !xSeq.is()) 736 continue; 737 sal_Int32 nLength( xSeq->getData().getLength()); 738 if( nLength > nResult ) 739 nResult = nLength; 740 } 741 } 742 743 return nResult; 744 } 745 746 OUString DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex ) const 747 { 748 if( nColumnIndex != -1 && 749 static_cast< sal_uInt32 >( nColumnIndex ) < m_aColumns.size()) 750 return m_aColumns[ nColumnIndex ].m_aUIRoleName; 751 return OUString(); 752 } 753 754 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex ) const 755 { 756 bool bIsCategories = false; 757 if( nColumnIndex>=0 && nColumnIndex<static_cast< sal_Int32 >(m_aColumns.size()) ) 758 bIsCategories = !m_aColumns[ nColumnIndex ].m_xDataSeries.is(); 759 return bIsCategories; 760 } 761 762 sal_Int32 DataBrowserModel::getCategoryColumnCount() 763 { 764 sal_Int32 nLastTextColumnIndex = -1; 765 tDataColumnVector::const_iterator aIt = m_aColumns.begin(); 766 for( ; aIt != m_aColumns.end(); ++aIt ) 767 { 768 if( !aIt->m_xDataSeries.is() ) 769 nLastTextColumnIndex++; 770 else 771 break; 772 } 773 return nLastTextColumnIndex+1; 774 } 775 776 const DataBrowserModel::tDataHeaderVector& DataBrowserModel::getDataHeaders() const 777 { 778 return m_aHeaders; 779 } 780 781 void DataBrowserModel::updateFromModel() 782 { 783 if( !m_xChartDocument.is()) 784 return; 785 m_aColumns.clear(); 786 m_aHeaders.clear(); 787 788 Reference< chart2::XDiagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDocument )); 789 if( !xDiagram.is()) 790 return; 791 792 // set template at DialogModel 793 uno::Reference< lang::XMultiServiceFactory > xFact( m_xChartDocument->getChartTypeManager(), uno::UNO_QUERY ); 794 DiagramHelper::tTemplateWithServiceName aTemplateAndService = 795 DiagramHelper::getTemplateForDiagram( xDiagram, xFact ); 796 if( aTemplateAndService.first.is()) 797 m_apDialogModel->setTemplate( aTemplateAndService.first ); 798 799 sal_Int32 nHeaderStart = 0; 800 sal_Int32 nHeaderEnd = 0; 801 if( lcl_ShowCategories( xDiagram )) 802 { 803 Reference< frame::XModel > xChartModel( m_xChartDocument, uno::UNO_QUERY ); 804 ExplicitCategoriesProvider aExplicitCategoriesProvider( ChartModelHelper::getFirstCoordinateSystem(xChartModel), xChartModel ); 805 806 const Sequence< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList( aExplicitCategoriesProvider.getSplitCategoriesList() ); 807 sal_Int32 nLevelCount = rSplitCategoriesList.getLength(); 808 for( sal_Int32 nL = 0; nL<nLevelCount; nL++ ) 809 { 810 Reference< chart2::data::XLabeledDataSequence > xCategories( rSplitCategoriesList[nL] ); 811 if( !xCategories.is() ) 812 continue; 813 814 tDataColumn aCategories; 815 aCategories.m_xLabeledDataSequence.set( xCategories ); 816 if( lcl_ShowCategoriesAsDataLabel( xDiagram )) 817 aCategories.m_aUIRoleName = DialogModel::GetRoleDataLabel(); 818 else 819 aCategories.m_aUIRoleName = lcl_getUIRoleName( xCategories ); 820 aCategories.m_eCellType = TEXTORDATE; 821 m_aColumns.push_back( aCategories ); 822 ++nHeaderStart; 823 } 824 } 825 826 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY ); 827 if( !xCooSysCnt.is()) 828 return; 829 Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems()); 830 for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx ) 831 { 832 Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW ); 833 Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes()); 834 sal_Int32 nXAxisNumberFormat = DataSeriesHelper::getNumberFormatKeyFromAxis( 0, aCooSysSeq[nCooSysIdx], 0, 0 ); 835 836 for( sal_Int32 nCTIdx=0; nCTIdx<aChartTypes.getLength(); ++nCTIdx ) 837 { 838 Reference< chart2::XDataSeriesContainer > xSeriesCnt( aChartTypes[nCTIdx], uno::UNO_QUERY ); 839 if( xSeriesCnt.is()) 840 { 841 rtl::OUString aRoleForDataLabelNumberFormat = ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( aChartTypes[nCTIdx] ); 842 843 Sequence< Reference< chart2::XDataSeries > > aSeries( xSeriesCnt->getDataSeries()); 844 lcl_tSharedSeqVec aSharedSequences( lcl_getSharedSequences( aSeries )); 845 for( lcl_tSharedSeqVec::const_iterator aIt( aSharedSequences.begin()); 846 aIt != aSharedSequences.end(); ++aIt ) 847 { 848 tDataColumn aSharedSequence; 849 aSharedSequence.m_xLabeledDataSequence = *aIt; 850 aSharedSequence.m_aUIRoleName = lcl_getUIRoleName( *aIt ); 851 aSharedSequence.m_eCellType = NUMBER; 852 // as the sequences are shared it should be ok to take the first series 853 // @todo: dimension index 0 for x-values used here. This is just a guess. 854 // Also, the axis index is 0, as there is usually only one x-axis 855 aSharedSequence.m_nNumberFormatKey = nXAxisNumberFormat; 856 m_aColumns.push_back( aSharedSequence ); 857 ++nHeaderStart; 858 } 859 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeries.getLength(); ++nSeriesIdx ) 860 { 861 tDataColumnVector::size_type nStartColIndex = m_aColumns.size(); 862 Reference< chart2::XDataSeries > xSeries( aSeries[nSeriesIdx] ); 863 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY ); 864 if( xSource.is()) 865 { 866 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeqs( xSource->getDataSequences()); 867 if( aLSeqs.getLength() == 0 ) 868 continue; 869 nHeaderEnd = nHeaderStart; 870 871 // @todo: dimension index 1 for y-values used here. This is just a guess 872 sal_Int32 nYAxisNumberFormatKey = 873 DataSeriesHelper::getNumberFormatKeyFromAxis( 874 aSeries[nSeriesIdx], aCooSysSeq[nCooSysIdx], 1 ); 875 876 sal_Int32 nSeqIdx=0; 877 for( ; nSeqIdx<aLSeqs.getLength(); ++nSeqIdx ) 878 { 879 sal_Int32 nSequenceNumberFormatKey = nYAxisNumberFormatKey; 880 OUString aRole = lcl_getRole( aLSeqs[nSeqIdx] ); 881 882 if( aRole.equals( aRoleForDataLabelNumberFormat ) ) 883 { 884 nSequenceNumberFormatKey = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel( 885 Reference< beans::XPropertySet >( xSeries, uno::UNO_QUERY ), xSeries, -1, xDiagram ); 886 } 887 else if( aRole.equals( C2U( "values-x" ) ) ) 888 nSequenceNumberFormatKey = nXAxisNumberFormat; 889 890 if( ::std::find_if( aSharedSequences.begin(), aSharedSequences.end(), 891 lcl_RepresentationsOfLSeqMatch( aLSeqs[nSeqIdx] )) == aSharedSequences.end()) 892 { 893 // no shared sequence 894 m_aColumns.push_back( 895 tDataColumn( 896 aSeries[nSeriesIdx], 897 nSeqIdx, 898 lcl_getUIRoleName( aLSeqs[nSeqIdx] ), 899 aLSeqs[nSeqIdx], 900 NUMBER, 901 nSequenceNumberFormatKey )); 902 ++nHeaderEnd; 903 } 904 // else skip 905 } 906 bool bSwapXAndYAxis = false; 907 try 908 { 909 Reference< beans::XPropertySet > xProp( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY ); 910 xProp->getPropertyValue( OUString(RTL_CONSTASCII_USTRINGPARAM("SwapXAndYAxis"))) >>= bSwapXAndYAxis; 911 } 912 catch( const beans::UnknownPropertyException & ex ) 913 { 914 (void)ex; 915 } 916 917 // add ranges for error bars if present for a series 918 if( StatisticsHelper::usesErrorBarRanges( aSeries[nSeriesIdx], /* bYError = */ true )) 919 addErrorBarRanges( aSeries[nSeriesIdx], nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd ); 920 921 m_aHeaders.push_back( 922 tDataHeader( 923 aSeries[nSeriesIdx], 924 aChartTypes[nCTIdx], 925 bSwapXAndYAxis, 926 nHeaderStart, 927 nHeaderEnd - 1 )); 928 929 nHeaderStart = nHeaderEnd; 930 931 ::std::sort( m_aColumns.begin() + nStartColIndex, m_aColumns.end(), implColumnLess() ); 932 } 933 } 934 } 935 } 936 } 937 } 938 939 void DataBrowserModel::addErrorBarRanges( 940 const Reference< chart2::XDataSeries > & xDataSeries, 941 sal_Int32 nNumberFormatKey, 942 sal_Int32 & rInOutSequenceIndex, 943 sal_Int32 & rInOutHeaderEnd ) 944 { 945 try 946 { 947 ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aSequences; 948 949 // x error bars 950 // ------------ 951 Reference< chart2::data::XDataSource > xErrorSource( 952 StatisticsHelper::getErrorBars( xDataSeries, /* bYError = */ false ), uno::UNO_QUERY ); 953 954 // positive x error bars 955 Reference< chart2::data::XLabeledDataSequence > xErrorLSequence( 956 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource( 957 xErrorSource, 958 /* bPositiveValue = */ true, 959 /* bYError = */ false )); 960 if( xErrorLSequence.is()) 961 aSequences.push_back( xErrorLSequence ); 962 963 // negative x error bars 964 xErrorLSequence.set( 965 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource( 966 xErrorSource, 967 /* bPositiveValue = */ false, 968 /* bYError = */ false )); 969 if( xErrorLSequence.is()) 970 aSequences.push_back( xErrorLSequence ); 971 972 // y error bars 973 // ------------ 974 xErrorSource.set( 975 StatisticsHelper::getErrorBars( xDataSeries, /* bYError = */ true ), uno::UNO_QUERY ); 976 977 // positive y error bars 978 xErrorLSequence.set( 979 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource( 980 xErrorSource, 981 /* bPositiveValue = */ true, 982 /* bYError = */ true )); 983 if( xErrorLSequence.is()) 984 aSequences.push_back( xErrorLSequence ); 985 986 // negative y error bars 987 xErrorLSequence.set( 988 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource( 989 xErrorSource, 990 /* bPositiveValue = */ false, 991 /* bYError = */ true )); 992 if( xErrorLSequence.is()) 993 aSequences.push_back( xErrorLSequence ); 994 995 996 for( ::std::vector< Reference< chart2::data::XLabeledDataSequence > >::const_iterator aIt( aSequences.begin()); 997 aIt != aSequences.end(); ++aIt ) 998 { 999 m_aColumns.push_back( 1000 tDataColumn( 1001 xDataSeries, 1002 rInOutSequenceIndex, 1003 lcl_getUIRoleName( *aIt ), 1004 *aIt, 1005 NUMBER, 1006 nNumberFormatKey )); 1007 ++rInOutSequenceIndex; 1008 ++rInOutHeaderEnd; 1009 } 1010 } 1011 catch( const uno::Exception & ex ) 1012 { 1013 ASSERT_EXCEPTION( ex ); 1014 } 1015 } 1016 1017 } // namespace chart 1018