1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <DiagramHelper.hxx> 21 #include <DataSeriesHelper.hxx> 22 #include <AxisHelper.hxx> 23 #include <ContainerHelper.hxx> 24 #include <ChartTypeHelper.hxx> 25 #include <ChartModel.hxx> 26 #include <ChartModelHelper.hxx> 27 #include <ExplicitCategoriesProvider.hxx> 28 #include <servicenames_charttypes.hxx> 29 #include <RelativePositionHelper.hxx> 30 #include <ControllerLockGuard.hxx> 31 #include <NumberFormatterWrapper.hxx> 32 #include <unonames.hxx> 33 34 #include <com/sun/star/chart/MissingValueTreatment.hpp> 35 #include <com/sun/star/chart/XDiagramPositioning.hpp> 36 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp> 37 #include <com/sun/star/chart2/XTitled.hpp> 38 #include <com/sun/star/chart2/XChartTypeContainer.hpp> 39 #include <com/sun/star/chart2/XChartTypeTemplate.hpp> 40 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> 41 #include <com/sun/star/chart2/XDataSeriesContainer.hpp> 42 #include <com/sun/star/chart2/AxisType.hpp> 43 #include <com/sun/star/chart2/DataPointGeometry3D.hpp> 44 #include <com/sun/star/chart2/RelativePosition.hpp> 45 #include <com/sun/star/chart2/RelativeSize.hpp> 46 #include <com/sun/star/chart2/StackingDirection.hpp> 47 48 #include <com/sun/star/util/CloseVetoException.hpp> 49 #include <com/sun/star/util/NumberFormat.hpp> 50 #include <com/sun/star/util/XNumberFormatsSupplier.hpp> 51 52 #include <unotools/saveopt.hxx> 53 #include <rtl/math.hxx> 54 #include <svl/zforlist.hxx> 55 #include <vcl/svapp.hxx> 56 #include <vcl/settings.hxx> 57 #include <comphelper/sequence.hxx> 58 #include <tools/diagnose_ex.h> 59 #include <sal/log.hxx> 60 61 using namespace ::com::sun::star; 62 using namespace ::com::sun::star::chart2; 63 using namespace ::std; 64 65 using ::com::sun::star::uno::Reference; 66 using ::com::sun::star::uno::Sequence; 67 using ::com::sun::star::uno::Any; 68 using ::com::sun::star::chart2::XAnyDescriptionAccess; 69 70 namespace chart 71 { 72 73 DiagramHelper::tTemplateWithServiceName 74 DiagramHelper::getTemplateForDiagram( 75 const Reference< XDiagram > & xDiagram, 76 const Reference< lang::XMultiServiceFactory > & xChartTypeManager ) 77 { 78 DiagramHelper::tTemplateWithServiceName aResult; 79 80 if( ! (xChartTypeManager.is() && xDiagram.is())) 81 return aResult; 82 83 Sequence< OUString > aServiceNames( xChartTypeManager->getAvailableServiceNames()); 84 const sal_Int32 nLength = aServiceNames.getLength(); 85 86 bool bTemplateFound = false; 87 88 for( sal_Int32 i = 0; ! bTemplateFound && i < nLength; ++i ) 89 { 90 try 91 { 92 Reference< XChartTypeTemplate > xTempl( 93 xChartTypeManager->createInstance( aServiceNames[ i ] ), uno::UNO_QUERY_THROW ); 94 95 if (xTempl.is() && xTempl->matchesTemplate(xDiagram, true)) 96 { 97 aResult.first = xTempl; 98 aResult.second = aServiceNames[ i ]; 99 bTemplateFound = true; 100 } 101 } 102 catch( const uno::Exception & ) 103 { 104 DBG_UNHANDLED_EXCEPTION("chart2"); 105 } 106 } 107 108 return aResult; 109 } 110 111 void DiagramHelper::setVertical( 112 const Reference< XDiagram > & xDiagram, 113 bool bVertical /* = true */ ) 114 { 115 try 116 { 117 Reference< XCoordinateSystemContainer > xCnt( xDiagram, uno::UNO_QUERY ); 118 if (!xCnt.is()) 119 return; 120 121 Sequence< Reference<XCoordinateSystem> > aCooSys = xCnt->getCoordinateSystems(); 122 uno::Any aValue; 123 aValue <<= bVertical; 124 for( sal_Int32 i=0; i<aCooSys.getLength(); ++i ) 125 { 126 uno::Reference< XCoordinateSystem > xCooSys( aCooSys[i] ); 127 Reference< beans::XPropertySet > xProp( xCooSys, uno::UNO_QUERY ); 128 bool bChanged = false; 129 if (xProp.is()) 130 { 131 bool bOldSwap = false; 132 if( !(xProp->getPropertyValue("SwapXAndYAxis") >>= bOldSwap) 133 || bVertical != bOldSwap ) 134 bChanged = true; 135 136 if( bChanged ) 137 xProp->setPropertyValue("SwapXAndYAxis", aValue); 138 } 139 140 if (!xCooSys.is()) 141 continue; 142 143 const sal_Int32 nDimensionCount = xCooSys->getDimension(); 144 sal_Int32 nDimIndex = 0; 145 for (nDimIndex=0; nDimIndex < nDimensionCount; ++nDimIndex) 146 { 147 const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nDimIndex); 148 for (sal_Int32 nI = 0; nI <= nMaximumScaleIndex; ++nI) 149 { 150 Reference<chart2::XAxis> xAxis = xCooSys->getAxisByDimension(nDimIndex,nI); 151 if (!xAxis.is()) 152 continue; 153 154 //adapt title rotation only when axis swapping has changed 155 if (!bChanged) 156 continue; 157 158 Reference< XTitled > xTitled( xAxis, uno::UNO_QUERY ); 159 if (!xTitled.is()) 160 continue; 161 162 Reference< beans::XPropertySet > xTitleProps( xTitled->getTitleObject(), uno::UNO_QUERY ); 163 if (!xTitleProps.is()) 164 continue; 165 166 double fAngleDegree = 0.0; 167 xTitleProps->getPropertyValue("TextRotation") >>= fAngleDegree; 168 if (fAngleDegree != 0.0 && 169 !rtl::math::approxEqual(fAngleDegree, 90.0)) 170 continue; 171 172 double fNewAngleDegree = 0.0; 173 if( !bVertical && nDimIndex == 1 ) 174 fNewAngleDegree = 90.0; 175 else if( bVertical && nDimIndex == 0 ) 176 fNewAngleDegree = 90.0; 177 178 xTitleProps->setPropertyValue("TextRotation", uno::Any(fNewAngleDegree)); 179 } 180 } 181 } 182 } 183 catch( const uno::Exception & ) 184 { 185 DBG_UNHANDLED_EXCEPTION("chart2"); 186 } 187 } 188 189 bool DiagramHelper::getVertical( const uno::Reference< chart2::XDiagram > & xDiagram, 190 bool& rbFound, bool& rbAmbiguous ) 191 { 192 bool bValue = false; 193 rbFound = false; 194 rbAmbiguous = false; 195 196 Reference< XCoordinateSystemContainer > xCnt( xDiagram, uno::UNO_QUERY ); 197 if (!xCnt.is()) 198 return false; 199 200 Sequence< Reference<XCoordinateSystem> > aCooSys = xCnt->getCoordinateSystems(); 201 202 for (sal_Int32 i = 0; i < aCooSys.getLength(); ++i) 203 { 204 Reference<beans::XPropertySet> xProp(aCooSys[i], uno::UNO_QUERY); 205 if (!xProp.is()) 206 continue; 207 208 bool bCurrent = false; 209 if (xProp->getPropertyValue("SwapXAndYAxis") >>= bCurrent) 210 { 211 if (!rbFound) 212 { 213 bValue = bCurrent; 214 rbFound = true; 215 } 216 else if (bCurrent != bValue) 217 { 218 // ambiguous -> choose always first found 219 rbAmbiguous = true; 220 } 221 } 222 } 223 return bValue; 224 } 225 226 void DiagramHelper::setStackMode( 227 const Reference< XDiagram > & xDiagram, 228 StackMode eStackMode 229 ) 230 { 231 try 232 { 233 bool bValueFound = false; 234 bool bIsAmbiguous = false; 235 StackMode eOldStackMode = DiagramHelper::getStackMode( xDiagram, bValueFound, bIsAmbiguous ); 236 237 if( eStackMode == eOldStackMode && !bIsAmbiguous ) 238 return; 239 240 StackingDirection eNewDirection = StackingDirection_NO_STACKING; 241 if( eStackMode == StackMode::YStacked || eStackMode == StackMode::YStackedPercent ) 242 eNewDirection = StackingDirection_Y_STACKING; 243 else if( eStackMode == StackMode::ZStacked ) 244 eNewDirection = StackingDirection_Z_STACKING; 245 246 uno::Any aNewDirection( eNewDirection ); 247 248 bool bPercent = false; 249 if( eStackMode == StackMode::YStackedPercent ) 250 bPercent = true; 251 252 //iterate through all coordinate systems 253 uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); 254 if( !xCooSysContainer.is() ) 255 return; 256 uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 257 for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS ) 258 { 259 uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] ); 260 //set correct percent stacking 261 const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(1); 262 for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI) 263 { 264 Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( 1,nI )); 265 if( xAxis.is()) 266 { 267 chart2::ScaleData aScaleData = xAxis->getScaleData(); 268 if( (aScaleData.AxisType==AxisType::PERCENT) != bPercent ) 269 { 270 if( bPercent ) 271 aScaleData.AxisType = AxisType::PERCENT; 272 else 273 aScaleData.AxisType = AxisType::REALNUMBER; 274 xAxis->setScaleData( aScaleData ); 275 } 276 } 277 } 278 //iterate through all chart types in the current coordinate system 279 uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY ); 280 if( !xChartTypeContainer.is() ) 281 continue; 282 uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 283 if (!aChartTypeList.getLength()) 284 continue; 285 286 uno::Reference< XChartType > xChartType( aChartTypeList[0] ); 287 288 //iterate through all series in this chart type 289 uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY ); 290 OSL_ASSERT( xDataSeriesContainer.is()); 291 if( !xDataSeriesContainer.is() ) 292 continue; 293 294 uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() ); 295 for( sal_Int32 nS = 0; nS < aSeriesList.getLength(); ++nS ) 296 { 297 Reference< beans::XPropertySet > xProp( aSeriesList[nS], uno::UNO_QUERY ); 298 if(xProp.is()) 299 xProp->setPropertyValue( "StackingDirection", aNewDirection ); 300 } 301 } 302 } 303 catch( const uno::Exception & ) 304 { 305 DBG_UNHANDLED_EXCEPTION("chart2"); 306 } 307 } 308 309 StackMode DiagramHelper::getStackMode( const Reference< XDiagram > & xDiagram, bool& rbFound, bool& rbAmbiguous ) 310 { 311 rbFound=false; 312 rbAmbiguous=false; 313 314 StackMode eGlobalStackMode = StackMode::NONE; 315 316 //iterate through all coordinate systems 317 uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); 318 if( !xCooSysContainer.is() ) 319 return eGlobalStackMode; 320 uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 321 for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS ) 322 { 323 uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] ); 324 325 //iterate through all chart types in the current coordinate system 326 uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY ); 327 if( !xChartTypeContainer.is() ) 328 continue; 329 uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 330 for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT ) 331 { 332 uno::Reference< XChartType > xChartType( aChartTypeList[nT] ); 333 334 StackMode eLocalStackMode = DiagramHelper::getStackModeFromChartType( 335 xChartType, rbFound, rbAmbiguous, xCooSys ); 336 337 if( rbFound && eLocalStackMode != eGlobalStackMode && nT>0 ) 338 { 339 rbAmbiguous = true; 340 return eGlobalStackMode; 341 } 342 343 eGlobalStackMode = eLocalStackMode; 344 } 345 } 346 347 return eGlobalStackMode; 348 } 349 350 StackMode DiagramHelper::getStackModeFromChartType( 351 const Reference< XChartType > & xChartType, 352 bool& rbFound, bool& rbAmbiguous, 353 const Reference< XCoordinateSystem > & xCorrespondingCoordinateSystem ) 354 { 355 StackMode eStackMode = StackMode::NONE; 356 rbFound = false; 357 rbAmbiguous = false; 358 359 try 360 { 361 Reference< XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY_THROW ); 362 Sequence< Reference< chart2::XDataSeries > > aSeries( xDSCnt->getDataSeries()); 363 364 chart2::StackingDirection eCommonDirection = chart2::StackingDirection_NO_STACKING; 365 bool bDirectionInitialized = false; 366 367 // first series is irrelevant for stacking, start with second, unless 368 // there is only one series 369 const sal_Int32 nSeriesCount = aSeries.getLength(); 370 sal_Int32 i = (nSeriesCount == 1) ? 0: 1; 371 for( ; i<nSeriesCount; ++i ) 372 { 373 rbFound = true; 374 Reference< beans::XPropertySet > xProp( aSeries[i], uno::UNO_QUERY_THROW ); 375 chart2::StackingDirection eCurrentDirection = eCommonDirection; 376 // property is not MAYBEVOID 377 bool bSuccess = ( xProp->getPropertyValue( "StackingDirection" ) >>= eCurrentDirection ); 378 OSL_ASSERT( bSuccess ); 379 if( ! bDirectionInitialized ) 380 { 381 eCommonDirection = eCurrentDirection; 382 bDirectionInitialized = true; 383 } 384 else 385 { 386 if( eCommonDirection != eCurrentDirection ) 387 { 388 rbAmbiguous = true; 389 break; 390 } 391 } 392 } 393 394 if( rbFound ) 395 { 396 if( eCommonDirection == chart2::StackingDirection_Z_STACKING ) 397 eStackMode = StackMode::ZStacked; 398 else if( eCommonDirection == chart2::StackingDirection_Y_STACKING ) 399 { 400 eStackMode = StackMode::YStacked; 401 402 // percent stacking 403 if( xCorrespondingCoordinateSystem.is() ) 404 { 405 if( 1 < xCorrespondingCoordinateSystem->getDimension() ) 406 { 407 sal_Int32 nAxisIndex = 0; 408 if( nSeriesCount ) 409 nAxisIndex = DataSeriesHelper::getAttachedAxisIndex(aSeries[0]); 410 411 Reference< chart2::XAxis > xAxis( 412 xCorrespondingCoordinateSystem->getAxisByDimension( 1,nAxisIndex )); 413 if( xAxis.is()) 414 { 415 chart2::ScaleData aScaleData = xAxis->getScaleData(); 416 if( aScaleData.AxisType==chart2::AxisType::PERCENT ) 417 eStackMode = StackMode::YStackedPercent; 418 } 419 } 420 } 421 } 422 } 423 } 424 catch( const uno::Exception & ) 425 { 426 DBG_UNHANDLED_EXCEPTION("chart2"); 427 } 428 429 return eStackMode; 430 } 431 432 sal_Int32 DiagramHelper::getDimension( const Reference< XDiagram > & xDiagram ) 433 { 434 // -1: not yet set 435 sal_Int32 nResult = -1; 436 437 try 438 { 439 Reference< XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY ); 440 if( xCooSysCnt.is() ) 441 { 442 Sequence< Reference< XCoordinateSystem > > aCooSysSeq( 443 xCooSysCnt->getCoordinateSystems()); 444 445 for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i ) 446 { 447 Reference< XCoordinateSystem > xCooSys( aCooSysSeq[i] ); 448 if(xCooSys.is()) 449 { 450 nResult = xCooSys->getDimension(); 451 break; 452 } 453 } 454 } 455 } 456 catch( const uno::Exception & ) 457 { 458 DBG_UNHANDLED_EXCEPTION("chart2"); 459 } 460 461 return nResult; 462 } 463 464 void DiagramHelper::setDimension( 465 const Reference< XDiagram > & xDiagram, 466 sal_Int32 nNewDimensionCount ) 467 { 468 if( ! xDiagram.is()) 469 return; 470 471 if( DiagramHelper::getDimension( xDiagram ) == nNewDimensionCount ) 472 return; 473 474 try 475 { 476 bool rbFound = false; 477 bool rbAmbiguous = true; 478 StackMode eStackMode = DiagramHelper::getStackMode( xDiagram, rbFound, rbAmbiguous ); 479 bool bIsSupportingOnlyDeepStackingFor3D=false; 480 481 //change all coordinate systems: 482 Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY_THROW ); 483 Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 484 for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS ) 485 { 486 Reference< XCoordinateSystem > xOldCooSys( aCooSysList[nCS], uno::UNO_QUERY ); 487 Reference< XCoordinateSystem > xNewCooSys; 488 489 Reference< XChartTypeContainer > xChartTypeContainer( xOldCooSys, uno::UNO_QUERY ); 490 if( !xChartTypeContainer.is() ) 491 continue; 492 493 Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 494 for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT ) 495 { 496 Reference< XChartType > xChartType( aChartTypeList[nT], uno::UNO_QUERY ); 497 bIsSupportingOnlyDeepStackingFor3D = ChartTypeHelper::isSupportingOnlyDeepStackingFor3D( xChartType ); 498 if(!xNewCooSys.is()) 499 { 500 xNewCooSys = xChartType->createCoordinateSystem( nNewDimensionCount ); 501 break; 502 } 503 //@todo make sure that all following charttypes are also capable of the new dimension 504 //otherwise separate them in a different group 505 //BM: might be done in replaceCoordinateSystem() 506 } 507 508 // replace the old coordinate system at all places where it was used 509 DiagramHelper::replaceCoordinateSystem( xDiagram, xOldCooSys, xNewCooSys ); 510 } 511 512 //correct stack mode if necessary 513 if( nNewDimensionCount==3 && eStackMode != StackMode::ZStacked && bIsSupportingOnlyDeepStackingFor3D ) 514 DiagramHelper::setStackMode( xDiagram, StackMode::ZStacked ); 515 else if( nNewDimensionCount==2 && eStackMode == StackMode::ZStacked ) 516 DiagramHelper::setStackMode( xDiagram, StackMode::NONE ); 517 } 518 catch( const uno::Exception & ) 519 { 520 DBG_UNHANDLED_EXCEPTION("chart2"); 521 } 522 } 523 524 void DiagramHelper::replaceCoordinateSystem( 525 const Reference< XDiagram > & xDiagram, 526 const Reference< XCoordinateSystem > & xCooSysToReplace, 527 const Reference< XCoordinateSystem > & xReplacement ) 528 { 529 OSL_ASSERT( xDiagram.is()); 530 if( ! xDiagram.is()) 531 return; 532 533 // update the coordinate-system container 534 Reference< XCoordinateSystemContainer > xCont( xDiagram, uno::UNO_QUERY ); 535 if( xCont.is()) 536 { 537 try 538 { 539 Reference< chart2::data::XLabeledDataSequence > xCategories = DiagramHelper::getCategoriesFromDiagram( xDiagram ); 540 541 // move chart types of xCooSysToReplace to xReplacement 542 Reference< XChartTypeContainer > xCTCntCooSys( xCooSysToReplace, uno::UNO_QUERY_THROW ); 543 Reference< XChartTypeContainer > xCTCntReplacement( xReplacement, uno::UNO_QUERY_THROW ); 544 xCTCntReplacement->setChartTypes( xCTCntCooSys->getChartTypes()); 545 546 xCont->removeCoordinateSystem( xCooSysToReplace ); 547 xCont->addCoordinateSystem( xReplacement ); 548 549 if( xCategories.is() ) 550 DiagramHelper::setCategoriesToDiagram( xCategories, xDiagram ); 551 } 552 catch( const uno::Exception & ) 553 { 554 DBG_UNHANDLED_EXCEPTION("chart2"); 555 } 556 } 557 } 558 559 bool DiagramHelper::isSeriesAttachedToMainAxis( 560 const uno::Reference< chart2::XDataSeries >& xDataSeries ) 561 { 562 sal_Int32 nAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries); 563 return (nAxisIndex==0); 564 } 565 566 bool DiagramHelper::attachSeriesToAxis( bool bAttachToMainAxis 567 , const uno::Reference< chart2::XDataSeries >& xDataSeries 568 , const uno::Reference< chart2::XDiagram >& xDiagram 569 , const uno::Reference< uno::XComponentContext > & xContext 570 , bool bAdaptAxes ) 571 { 572 bool bChanged = false; 573 574 //set property at axis 575 Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW ); 576 577 sal_Int32 nNewAxisIndex = bAttachToMainAxis ? 0 : 1; 578 sal_Int32 nOldAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries); 579 uno::Reference< chart2::XAxis > xOldAxis( DiagramHelper::getAttachedAxis( xDataSeries, xDiagram ) ); 580 581 if( nOldAxisIndex != nNewAxisIndex ) 582 { 583 try 584 { 585 xProp->setPropertyValue( "AttachedAxisIndex", uno::Any( nNewAxisIndex ) ); 586 bChanged = true; 587 } 588 catch( const uno::Exception & ) 589 { 590 DBG_UNHANDLED_EXCEPTION("chart2"); 591 } 592 } 593 594 if( bChanged && xDiagram.is() ) 595 { 596 uno::Reference< XAxis > xAxis( AxisHelper::getAxis( 1, bAttachToMainAxis, xDiagram ) ); 597 if(!xAxis.is()) //create an axis if necessary 598 xAxis = AxisHelper::createAxis( 1, bAttachToMainAxis, xDiagram, xContext ); 599 if( bAdaptAxes ) 600 { 601 AxisHelper::makeAxisVisible( xAxis ); 602 AxisHelper::hideAxisIfNoDataIsAttached( xOldAxis, xDiagram ); 603 } 604 } 605 606 return bChanged; 607 } 608 609 uno::Reference< XAxis > DiagramHelper::getAttachedAxis( 610 const uno::Reference< XDataSeries >& xSeries, 611 const uno::Reference< XDiagram >& xDiagram ) 612 { 613 return AxisHelper::getAxis( 1, DiagramHelper::isSeriesAttachedToMainAxis( xSeries ), xDiagram ); 614 } 615 616 uno::Reference< XChartType > DiagramHelper::getChartTypeOfSeries( 617 const uno::Reference< chart2::XDiagram >& xDiagram 618 , const uno::Reference< XDataSeries >& xGivenDataSeries ) 619 { 620 if( !xGivenDataSeries.is() ) 621 return nullptr; 622 if(!xDiagram.is()) 623 return nullptr; 624 625 //iterate through the model to find the given xSeries 626 //the found parent indicates the charttype 627 628 //iterate through all coordinate systems 629 uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); 630 if( !xCooSysContainer.is()) 631 return nullptr; 632 633 uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 634 for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS ) 635 { 636 uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] ); 637 638 //iterate through all chart types in the current coordinate system 639 uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY ); 640 OSL_ASSERT( xChartTypeContainer.is()); 641 if( !xChartTypeContainer.is() ) 642 continue; 643 uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 644 for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT ) 645 { 646 uno::Reference< XChartType > xChartType( aChartTypeList[nT] ); 647 648 //iterate through all series in this chart type 649 uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY ); 650 OSL_ASSERT( xDataSeriesContainer.is()); 651 if( !xDataSeriesContainer.is() ) 652 continue; 653 654 uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() ); 655 for( sal_Int32 nS = 0; nS < aSeriesList.getLength(); ++nS ) 656 { 657 if( xGivenDataSeries==aSeriesList[nS] ) 658 return xChartType; 659 } 660 } 661 } 662 return nullptr; 663 } 664 665 std::vector< Reference< XDataSeries > > 666 DiagramHelper::getDataSeriesFromDiagram( 667 const Reference< XDiagram > & xDiagram ) 668 { 669 std::vector< Reference< XDataSeries > > aResult; 670 671 try 672 { 673 Reference< XCoordinateSystemContainer > xCooSysCnt( 674 xDiagram, uno::UNO_QUERY_THROW ); 675 Sequence< Reference< XCoordinateSystem > > aCooSysSeq( 676 xCooSysCnt->getCoordinateSystems()); 677 for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i ) 678 { 679 Reference< XChartTypeContainer > xCTCnt( aCooSysSeq[i], uno::UNO_QUERY_THROW ); 680 Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes()); 681 for( sal_Int32 j=0; j<aChartTypeSeq.getLength(); ++j ) 682 { 683 Reference< XDataSeriesContainer > xDSCnt( aChartTypeSeq[j], uno::UNO_QUERY_THROW ); 684 Sequence< Reference< XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries() ); 685 std::copy( aSeriesSeq.begin(), aSeriesSeq.end(), 686 std::back_inserter( aResult )); 687 } 688 } 689 } 690 catch( const uno::Exception & ) 691 { 692 DBG_UNHANDLED_EXCEPTION("chart2"); 693 } 694 695 return aResult; 696 } 697 698 Sequence< Sequence< Reference< XDataSeries > > > 699 DiagramHelper::getDataSeriesGroups( const Reference< XDiagram > & xDiagram ) 700 { 701 vector< Sequence< Reference< XDataSeries > > > aResult; 702 703 //iterate through all coordinate systems 704 Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); 705 if( xCooSysContainer.is() ) 706 { 707 Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 708 for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS ) 709 { 710 //iterate through all chart types in the current coordinate system 711 Reference< XChartTypeContainer > xChartTypeContainer( aCooSysList[nCS], uno::UNO_QUERY ); 712 if( !xChartTypeContainer.is() ) 713 continue; 714 Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 715 for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT ) 716 { 717 Reference< XDataSeriesContainer > xDataSeriesContainer( aChartTypeList[nT], uno::UNO_QUERY ); 718 if( !xDataSeriesContainer.is() ) 719 continue; 720 aResult.push_back( xDataSeriesContainer->getDataSeries() ); 721 } 722 } 723 } 724 return comphelper::containerToSequence( aResult ); 725 } 726 727 Reference< XChartType > 728 DiagramHelper::getChartTypeByIndex( const Reference< XDiagram >& xDiagram, sal_Int32 nIndex ) 729 { 730 Reference< XChartType > xChartType; 731 732 //iterate through all coordinate systems 733 Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); 734 if( ! xCooSysContainer.is()) 735 return xChartType; 736 737 Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 738 sal_Int32 nTypesSoFar = 0; 739 for( sal_Int32 nCS = 0; nCS < aCooSysList.getLength(); ++nCS ) 740 { 741 Reference< XChartTypeContainer > xChartTypeContainer( aCooSysList[nCS], uno::UNO_QUERY ); 742 if( !xChartTypeContainer.is() ) 743 continue; 744 Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 745 if( nIndex >= 0 && nIndex < (nTypesSoFar + aChartTypeList.getLength()) ) 746 { 747 xChartType.set( aChartTypeList[nIndex - nTypesSoFar] ); 748 break; 749 } 750 nTypesSoFar += aChartTypeList.getLength(); 751 } 752 753 return xChartType; 754 } 755 756 namespace 757 { 758 759 std::vector< Reference< XAxis > > lcl_getAxisHoldingCategoriesFromDiagram( 760 const Reference< XDiagram > & xDiagram ) 761 { 762 std::vector< Reference< XAxis > > aRet; 763 764 // return first x-axis as fall-back 765 Reference< XAxis > xFallBack; 766 try 767 { 768 Reference< XCoordinateSystemContainer > xCooSysCnt( 769 xDiagram, uno::UNO_QUERY_THROW ); 770 Sequence< Reference< XCoordinateSystem > > aCooSysSeq( 771 xCooSysCnt->getCoordinateSystems()); 772 for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i ) 773 { 774 Reference< XCoordinateSystem > xCooSys( aCooSysSeq[i] ); 775 OSL_ASSERT( xCooSys.is()); 776 for( sal_Int32 nN = xCooSys->getDimension(); nN--; ) 777 { 778 const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nN); 779 for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI) 780 { 781 Reference< XAxis > xAxis = xCooSys->getAxisByDimension( nN,nI ); 782 OSL_ASSERT( xAxis.is()); 783 if( xAxis.is()) 784 { 785 ScaleData aScaleData = xAxis->getScaleData(); 786 if( aScaleData.Categories.is() || (aScaleData.AxisType == AxisType::CATEGORY) ) 787 { 788 aRet.push_back(xAxis); 789 } 790 if( (nN == 0) && !xFallBack.is()) 791 xFallBack.set( xAxis ); 792 } 793 } 794 } 795 } 796 } 797 catch( const uno::Exception & ) 798 { 799 DBG_UNHANDLED_EXCEPTION("chart2" ); 800 } 801 802 if( aRet.empty() ) 803 aRet.push_back(xFallBack); 804 805 return aRet; 806 } 807 808 } // anonymous namespace 809 810 bool DiagramHelper::isCategoryDiagram( 811 const Reference< XDiagram >& xDiagram ) 812 { 813 try 814 { 815 Reference< XCoordinateSystemContainer > xCooSysCnt( 816 xDiagram, uno::UNO_QUERY_THROW ); 817 Sequence< Reference< XCoordinateSystem > > aCooSysSeq( 818 xCooSysCnt->getCoordinateSystems()); 819 for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i ) 820 { 821 Reference< XCoordinateSystem > xCooSys( aCooSysSeq[i] ); 822 OSL_ASSERT( xCooSys.is()); 823 for( sal_Int32 nN = xCooSys->getDimension(); nN--; ) 824 { 825 const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nN); 826 for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI) 827 { 828 Reference< XAxis > xAxis = xCooSys->getAxisByDimension( nN,nI ); 829 OSL_ASSERT( xAxis.is()); 830 if( xAxis.is()) 831 { 832 ScaleData aScaleData = xAxis->getScaleData(); 833 if( aScaleData.AxisType == AxisType::CATEGORY || aScaleData.AxisType == AxisType::DATE ) 834 return true; 835 } 836 } 837 } 838 } 839 } 840 catch( const uno::Exception & ) 841 { 842 DBG_UNHANDLED_EXCEPTION("chart2"); 843 } 844 845 return false; 846 } 847 848 void DiagramHelper::setCategoriesToDiagram( 849 const Reference< chart2::data::XLabeledDataSequence >& xCategories, 850 const Reference< XDiagram >& xDiagram, 851 bool bSetAxisType /* = false */, 852 bool bCategoryAxis /* = true */ ) 853 { 854 std::vector< Reference< chart2::XAxis > > aCatAxes( 855 lcl_getAxisHoldingCategoriesFromDiagram( xDiagram )); 856 857 for (const Reference< chart2::XAxis >& xCatAxis : aCatAxes) 858 { 859 if( xCatAxis.is()) 860 { 861 ScaleData aScaleData( xCatAxis->getScaleData()); 862 aScaleData.Categories = xCategories; 863 if( bSetAxisType ) 864 { 865 if( bCategoryAxis ) 866 aScaleData.AxisType = AxisType::CATEGORY; 867 else if( aScaleData.AxisType == AxisType::CATEGORY || aScaleData.AxisType == AxisType::DATE ) 868 aScaleData.AxisType = AxisType::REALNUMBER; 869 } 870 xCatAxis->setScaleData( aScaleData ); 871 } 872 } 873 } 874 875 Reference< data::XLabeledDataSequence > 876 DiagramHelper::getCategoriesFromDiagram( 877 const Reference< XDiagram > & xDiagram ) 878 { 879 Reference< data::XLabeledDataSequence > xResult; 880 881 try 882 { 883 std::vector< Reference< chart2::XAxis > > aCatAxes( 884 lcl_getAxisHoldingCategoriesFromDiagram( xDiagram )); 885 //search for first categories 886 if (!aCatAxes.empty()) 887 { 888 Reference< chart2::XAxis > xCatAxis(aCatAxes[0]); 889 if( xCatAxis.is()) 890 { 891 ScaleData aScaleData( xCatAxis->getScaleData()); 892 if( aScaleData.Categories.is() ) 893 { 894 xResult.set( aScaleData.Categories ); 895 uno::Reference<beans::XPropertySet> xProp(aScaleData.Categories->getValues(), uno::UNO_QUERY ); 896 if( xProp.is() ) 897 { 898 try 899 { 900 xProp->setPropertyValue( "Role", uno::Any( OUString("categories") ) ); 901 } 902 catch( const uno::Exception & ) 903 { 904 DBG_UNHANDLED_EXCEPTION("chart2"); 905 } 906 } 907 } 908 } 909 } 910 } 911 catch( const uno::Exception & ) 912 { 913 DBG_UNHANDLED_EXCEPTION("chart2"); 914 } 915 916 return xResult; 917 } 918 919 static void lcl_generateAutomaticCategoriesFromChartType( 920 Sequence< OUString >& rRet, 921 const Reference< XChartType >& xChartType ) 922 { 923 if(!xChartType.is()) 924 return; 925 OUString aMainSeq( xChartType->getRoleOfSequenceForSeriesLabel() ); 926 Reference< XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY ); 927 if( xSeriesCnt.is() ) 928 { 929 Sequence< Reference< XDataSeries > > aSeriesSeq( xSeriesCnt->getDataSeries() ); 930 for( sal_Int32 nS = 0; nS < aSeriesSeq.getLength(); nS++ ) 931 { 932 Reference< data::XDataSource > xDataSource( aSeriesSeq[nS], uno::UNO_QUERY ); 933 if( !xDataSource.is() ) 934 continue; 935 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( 936 ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, aMainSeq )); 937 if( !xLabeledSeq.is() ) 938 continue; 939 Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues() ); 940 if( !xValueSeq.is() ) 941 continue; 942 rRet = xValueSeq->generateLabel( chart2::data::LabelOrigin_LONG_SIDE ); 943 if( rRet.getLength() ) 944 return; 945 } 946 } 947 } 948 949 Sequence< OUString > DiagramHelper::generateAutomaticCategoriesFromCooSys( const Reference< XCoordinateSystem > & xCooSys ) 950 { 951 Sequence< OUString > aRet; 952 953 Reference< XChartTypeContainer > xTypeCntr( xCooSys, uno::UNO_QUERY ); 954 if( xTypeCntr.is() ) 955 { 956 Sequence< Reference< XChartType > > aChartTypes( xTypeCntr->getChartTypes() ); 957 for( sal_Int32 nN=0; nN<aChartTypes.getLength(); nN++ ) 958 { 959 lcl_generateAutomaticCategoriesFromChartType( aRet, aChartTypes[nN] ); 960 if( aRet.getLength() ) 961 return aRet; 962 } 963 } 964 return aRet; 965 } 966 967 Sequence< OUString > DiagramHelper::getExplicitSimpleCategories( 968 ChartModel& rModel ) 969 { 970 uno::Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( rModel ) ); 971 ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSys, rModel ); 972 return aExplicitCategoriesProvider.getSimpleCategories(); 973 } 974 975 namespace 976 { 977 void lcl_switchToDateCategories( const Reference< XChartDocument >& xChartDoc, const Reference< XAxis >& xAxis ) 978 { 979 if( !xAxis.is() ) 980 return; 981 if( !xChartDoc.is() ) 982 return; 983 984 ScaleData aScale( xAxis->getScaleData() ); 985 if( xChartDoc->hasInternalDataProvider() ) 986 { 987 //remove all content the is not of type double and remove multiple level 988 Reference< XAnyDescriptionAccess > xDataAccess( xChartDoc->getDataProvider(), uno::UNO_QUERY ); 989 if( xDataAccess.is() ) 990 { 991 Sequence< Sequence< Any > > aAnyCategories( xDataAccess->getAnyRowDescriptions() ); 992 double fTest = 0.0; 993 double fNan = 0.0; 994 ::rtl::math::setNan( & fNan ); 995 sal_Int32 nN = aAnyCategories.getLength(); 996 for( ; nN--; ) 997 { 998 Sequence< Any >& rCat = aAnyCategories[nN]; 999 if( rCat.getLength() > 1 ) 1000 rCat.realloc(1); 1001 if( rCat.getLength() == 1 ) 1002 { 1003 Any& rAny = rCat[0]; 1004 if( !(rAny>>=fTest) ) 1005 { 1006 rAny <<= fNan; 1007 } 1008 } 1009 } 1010 xDataAccess->setAnyRowDescriptions( aAnyCategories ); 1011 } 1012 //check the numberformat at the axis 1013 Reference< beans::XPropertySet > xAxisProps( xAxis, uno::UNO_QUERY ); 1014 Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( xChartDoc, uno::UNO_QUERY ); 1015 if( xAxisProps.is() && xNumberFormatsSupplier.is() ) 1016 { 1017 sal_Int32 nNumberFormat = -1; 1018 xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormat; 1019 1020 Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() ); 1021 if( xNumberFormats.is() ) 1022 { 1023 Reference< beans::XPropertySet > xKeyProps; 1024 try 1025 { 1026 xKeyProps = xNumberFormats->getByKey( nNumberFormat ); 1027 } 1028 catch( const uno::Exception & ) 1029 { 1030 DBG_UNHANDLED_EXCEPTION("chart2"); 1031 } 1032 sal_Int32 nType = util::NumberFormat::UNDEFINED; 1033 if( xKeyProps.is() ) 1034 xKeyProps->getPropertyValue( "Type" ) >>= nType; 1035 if( !( nType & util::NumberFormat::DATE ) ) 1036 { 1037 //set a date format to the axis 1038 const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper(); 1039 Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::DATE, rLocaleDataWrapper.getLanguageTag().getLocale(), true/*bCreate*/ ); 1040 if( aKeySeq.getLength() ) 1041 { 1042 xAxisProps->setPropertyValue(CHART_UNONAME_NUMFMT, uno::Any(aKeySeq[0])); 1043 } 1044 } 1045 } 1046 } 1047 } 1048 if( aScale.AxisType != chart2::AxisType::DATE ) 1049 AxisHelper::removeExplicitScaling( aScale ); 1050 aScale.AxisType = chart2::AxisType::DATE; 1051 xAxis->setScaleData( aScale ); 1052 } 1053 1054 void lcl_switchToTextCategories( const Reference< XChartDocument >& xChartDoc, const Reference< XAxis >& xAxis ) 1055 { 1056 if( !xAxis.is() ) 1057 return; 1058 if( !xChartDoc.is() ) 1059 return; 1060 ScaleData aScale( xAxis->getScaleData() ); 1061 if( aScale.AxisType != chart2::AxisType::CATEGORY ) 1062 AxisHelper::removeExplicitScaling( aScale ); 1063 //todo migrate dates to text? 1064 aScale.AxisType = chart2::AxisType::CATEGORY; 1065 aScale.AutoDateAxis = false; 1066 xAxis->setScaleData( aScale ); 1067 } 1068 1069 } 1070 1071 void DiagramHelper::switchToDateCategories( const Reference< XChartDocument >& xChartDoc ) 1072 { 1073 Reference< frame::XModel > xChartModel( xChartDoc, uno::UNO_QUERY ); 1074 if(xChartModel.is()) 1075 { 1076 ControllerLockGuardUNO aCtrlLockGuard( xChartModel ); 1077 1078 Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( xChartModel ) ); 1079 if( xCooSys.is() ) 1080 { 1081 Reference< XAxis > xAxis( xCooSys->getAxisByDimension(0,0) ); 1082 lcl_switchToDateCategories( xChartDoc, xAxis ); 1083 } 1084 } 1085 } 1086 1087 void DiagramHelper::switchToTextCategories( const Reference< XChartDocument >& xChartDoc ) 1088 { 1089 Reference< frame::XModel > xChartModel( xChartDoc, uno::UNO_QUERY ); 1090 if(xChartModel.is()) 1091 { 1092 ControllerLockGuardUNO aCtrlLockGuard( xChartModel ); 1093 1094 Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( xChartModel ) ); 1095 if( xCooSys.is() ) 1096 { 1097 Reference< XAxis > xAxis( xCooSys->getAxisByDimension(0,0) ); 1098 lcl_switchToTextCategories( xChartDoc, xAxis ); 1099 } 1100 } 1101 } 1102 1103 bool DiagramHelper::isSupportingDateAxis( const Reference< chart2::XDiagram >& xDiagram ) 1104 { 1105 return ::chart::ChartTypeHelper::isSupportingDateAxis( 1106 DiagramHelper::getChartTypeByIndex( xDiagram, 0 ), 0 ); 1107 } 1108 1109 bool DiagramHelper::isDateNumberFormat( sal_Int32 nNumberFormat, const Reference< util::XNumberFormats >& xNumberFormats ) 1110 { 1111 bool bIsDate = false; 1112 if( !xNumberFormats.is() ) 1113 return bIsDate; 1114 1115 Reference< beans::XPropertySet > xKeyProps = xNumberFormats->getByKey( nNumberFormat ); 1116 if( xKeyProps.is() ) 1117 { 1118 sal_Int32 nType = util::NumberFormat::UNDEFINED; 1119 xKeyProps->getPropertyValue( "Type" ) >>= nType; 1120 bIsDate = nType & util::NumberFormat::DATE; 1121 } 1122 return bIsDate; 1123 } 1124 1125 sal_Int32 DiagramHelper::getDateNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier ) 1126 { 1127 sal_Int32 nRet=-1; 1128 1129 //try to get a date format with full year display 1130 const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag(); 1131 NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier ); 1132 SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter(); 1133 if( pNumFormatter ) 1134 { 1135 nRet = pNumFormatter->GetFormatIndex( NF_DATE_SYS_DDMMYYYY, rLanguageTag.getLanguageType() ); 1136 } 1137 else 1138 { 1139 Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() ); 1140 if( xNumberFormats.is() ) 1141 { 1142 Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::DATE, 1143 rLanguageTag.getLocale(), true/*bCreate */); 1144 if( aKeySeq.getLength() ) 1145 { 1146 nRet = aKeySeq[0]; 1147 } 1148 } 1149 } 1150 return nRet; 1151 } 1152 1153 sal_Int32 DiagramHelper::getDateTimeInputNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier, double fNumber ) 1154 { 1155 sal_Int32 nRet = 0; 1156 1157 // Get the most detailed date/time format according to fNumber. 1158 NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier ); 1159 SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter(); 1160 if (!pNumFormatter) 1161 SAL_WARN("chart2", "DiagramHelper::getDateTimeInputNumberFormat - no SvNumberFormatter"); 1162 else 1163 { 1164 SvNumFormatType nType; 1165 // Obtain best matching date, time or datetime format. 1166 nRet = pNumFormatter->GuessDateTimeFormat( nType, fNumber, LANGUAGE_SYSTEM); 1167 // Obtain the corresponding edit format. 1168 nRet = pNumFormatter->GetEditFormat( fNumber, nRet, nType, LANGUAGE_SYSTEM, nullptr); 1169 } 1170 return nRet; 1171 } 1172 1173 sal_Int32 DiagramHelper::getPercentNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier ) 1174 { 1175 sal_Int32 nRet=-1; 1176 const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag(); 1177 NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier ); 1178 SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter(); 1179 if( pNumFormatter ) 1180 { 1181 nRet = pNumFormatter->GetFormatIndex( NF_PERCENT_INT, rLanguageTag.getLanguageType() ); 1182 } 1183 else 1184 { 1185 Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() ); 1186 if( xNumberFormats.is() ) 1187 { 1188 Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::PERCENT, 1189 rLanguageTag.getLocale(), true/*bCreate*/ ); 1190 if( aKeySeq.getLength() ) 1191 { 1192 // This *assumes* the sequence is sorted as in 1193 // NfIndexTableOffset and the first format is the integer 0% 1194 // format by chance.. which usually is the case, but.. anyway, 1195 // we usually also have a number formatter so don't reach here. 1196 nRet = aKeySeq[0]; 1197 } 1198 } 1199 } 1200 return nRet; 1201 } 1202 1203 Sequence< Reference< XChartType > > 1204 DiagramHelper::getChartTypesFromDiagram( 1205 const Reference< XDiagram > & xDiagram ) 1206 { 1207 std::vector< Reference< XChartType > > aResult; 1208 1209 if(xDiagram.is()) 1210 { 1211 try 1212 { 1213 Reference< XCoordinateSystemContainer > xCooSysCnt( 1214 xDiagram, uno::UNO_QUERY_THROW ); 1215 Sequence< Reference< XCoordinateSystem > > aCooSysSeq( 1216 xCooSysCnt->getCoordinateSystems()); 1217 for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i ) 1218 { 1219 Reference< XChartTypeContainer > xCTCnt( aCooSysSeq[i], uno::UNO_QUERY_THROW ); 1220 Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes()); 1221 std::copy( aChartTypeSeq.begin(), aChartTypeSeq.end(), 1222 std::back_inserter( aResult )); 1223 } 1224 } 1225 catch( const uno::Exception & ) 1226 { 1227 DBG_UNHANDLED_EXCEPTION("chart2"); 1228 } 1229 } 1230 1231 return comphelper::containerToSequence( aResult ); 1232 } 1233 1234 bool DiagramHelper::areChartTypesCompatible( const Reference< ::chart2::XChartType >& xFirstType, 1235 const Reference< ::chart2::XChartType >& xSecondType ) 1236 { 1237 if( !xFirstType.is() || !xSecondType.is() ) 1238 return false; 1239 1240 std::vector< OUString > aFirstRoles( ContainerHelper::SequenceToVector( xFirstType->getSupportedMandatoryRoles() ) ); 1241 std::vector< OUString > aSecondRoles( ContainerHelper::SequenceToVector( xSecondType->getSupportedMandatoryRoles() ) ); 1242 std::sort( aFirstRoles.begin(), aFirstRoles.end() ); 1243 std::sort( aSecondRoles.begin(), aSecondRoles.end() ); 1244 return ( aFirstRoles == aSecondRoles ); 1245 } 1246 1247 namespace 1248 { 1249 /** 1250 * This method implements the logic of checking if a series can be moved 1251 * forward/backward. Depending on the "bDoMove" parameter the series will 1252 * be moved (bDoMove = true) or the function just will test if the 1253 * series can be moved without doing the move (bDoMove = false). 1254 * 1255 * @param xDiagram 1256 * Reference to the diagram that contains the series. 1257 * 1258 * @param xGivenDataSeries 1259 * Reference to the series that should moved or tested for moving. 1260 * 1261 * @param bForward 1262 * Direction in which the series should be moved or tested for moving. 1263 * 1264 * @param bDoMove 1265 * Should this function really move the series (true) or just test if it is 1266 * possible (false). 1267 * 1268 * 1269 * @returns 1270 * in case of bDoMove == true 1271 * - True : if the move was done 1272 * - False : the move failed 1273 * in case of bDoMove == false 1274 * - True : the series can be moved 1275 * - False : the series can not be moved 1276 * 1277 */ 1278 1279 bool lcl_moveSeriesOrCheckIfMoveIsAllowed( 1280 const Reference< XDiagram >& xDiagram, 1281 const Reference< XDataSeries >& xGivenDataSeries, 1282 bool bForward, 1283 bool bDoMove ) 1284 { 1285 bool bMovedOrMoveAllowed = false; 1286 1287 try 1288 { 1289 uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); 1290 1291 if( xGivenDataSeries.is() && xCooSysContainer.is() ) 1292 { 1293 //find position of series. 1294 bool bFound = false; 1295 uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); 1296 1297 for( sal_Int32 nCS = 0; !bFound && nCS < aCooSysList.getLength(); ++nCS ) 1298 { 1299 uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] ); 1300 1301 //iterate through all chart types in the current coordinate system 1302 uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY ); 1303 OSL_ASSERT( xChartTypeContainer.is()); 1304 if( !xChartTypeContainer.is() ) 1305 continue; 1306 uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); 1307 uno::Reference< XChartType > xFormerChartType; 1308 1309 for( sal_Int32 nT = 0; !bFound && nT < aChartTypeList.getLength(); ++nT ) 1310 { 1311 uno::Reference< XChartType > xCurrentChartType( aChartTypeList[nT] ); 1312 1313 //iterate through all series in this chart type 1314 uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xCurrentChartType, uno::UNO_QUERY ); 1315 OSL_ASSERT( xDataSeriesContainer.is()); 1316 if( !xDataSeriesContainer.is() ) 1317 continue; 1318 1319 uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() ); 1320 1321 for( sal_Int32 nS = 0; !bFound && nS < aSeriesList.getLength(); ++nS ) 1322 { 1323 1324 // We found the series we are interested in! 1325 if( xGivenDataSeries==aSeriesList[nS] ) 1326 { 1327 sal_Int32 nOldSeriesIndex = nS; 1328 bFound = true; 1329 1330 try 1331 { 1332 sal_Int32 nNewSeriesIndex = nS; 1333 1334 if( bForward ) 1335 nNewSeriesIndex--; 1336 else 1337 nNewSeriesIndex++; 1338 1339 if( nNewSeriesIndex >= 0 && nNewSeriesIndex < aSeriesList.getLength() ) 1340 { 1341 //move series in the same charttype 1342 bMovedOrMoveAllowed = true; 1343 if( bDoMove ) 1344 { 1345 aSeriesList[ nOldSeriesIndex ] = aSeriesList[ nNewSeriesIndex ]; 1346 aSeriesList[ nNewSeriesIndex ] = xGivenDataSeries; 1347 xDataSeriesContainer->setDataSeries( aSeriesList ); 1348 } 1349 } 1350 else if( nNewSeriesIndex<0 ) 1351 { 1352 //exchange series with former charttype 1353 if( xFormerChartType.is() && DiagramHelper::areChartTypesCompatible( xFormerChartType, xCurrentChartType ) ) 1354 { 1355 bMovedOrMoveAllowed = true; 1356 if( bDoMove ) 1357 { 1358 uno::Reference< XDataSeriesContainer > xOtherDataSeriesContainer( xFormerChartType, uno::UNO_QUERY ); 1359 if( xOtherDataSeriesContainer.is() ) 1360 { 1361 uno::Sequence< uno::Reference< XDataSeries > > aOtherSeriesList( xOtherDataSeriesContainer->getDataSeries() ); 1362 sal_Int32 nOtherSeriesIndex = aOtherSeriesList.getLength()-1; 1363 if( nOtherSeriesIndex >= 0 && nOtherSeriesIndex < aOtherSeriesList.getLength() ) 1364 { 1365 uno::Reference< XDataSeries > xExchangeSeries( aOtherSeriesList[nOtherSeriesIndex] ); 1366 aOtherSeriesList[nOtherSeriesIndex] = xGivenDataSeries; 1367 xOtherDataSeriesContainer->setDataSeries(aOtherSeriesList); 1368 1369 aSeriesList[nOldSeriesIndex]=xExchangeSeries; 1370 xDataSeriesContainer->setDataSeries(aSeriesList); 1371 } 1372 } 1373 } 1374 } 1375 } 1376 else if( nT+1 < aChartTypeList.getLength() ) 1377 { 1378 //exchange series with next charttype 1379 uno::Reference< XChartType > xOtherChartType( aChartTypeList[nT+1] ); 1380 if( xOtherChartType.is() && DiagramHelper::areChartTypesCompatible( xOtherChartType, xCurrentChartType ) ) 1381 { 1382 bMovedOrMoveAllowed = true; 1383 if( bDoMove ) 1384 { 1385 uno::Reference< XDataSeriesContainer > xOtherDataSeriesContainer( xOtherChartType, uno::UNO_QUERY ); 1386 if( xOtherDataSeriesContainer.is() ) 1387 { 1388 uno::Sequence< uno::Reference< XDataSeries > > aOtherSeriesList( xOtherDataSeriesContainer->getDataSeries() ); 1389 if( 0 < aOtherSeriesList.getLength() ) 1390 { 1391 uno::Reference< XDataSeries > xExchangeSeries( aOtherSeriesList[0] ); 1392 aOtherSeriesList[0] = xGivenDataSeries; 1393 xOtherDataSeriesContainer->setDataSeries(aOtherSeriesList); 1394 1395 aSeriesList[nOldSeriesIndex]=xExchangeSeries; 1396 xDataSeriesContainer->setDataSeries(aSeriesList); 1397 } 1398 } 1399 } 1400 } 1401 } 1402 } 1403 catch( const util::CloseVetoException& ) 1404 { 1405 } 1406 catch( const uno::RuntimeException& ) 1407 { 1408 } 1409 } 1410 } 1411 xFormerChartType = xCurrentChartType; 1412 } 1413 } 1414 } 1415 } 1416 catch( const util::CloseVetoException& ) 1417 { 1418 } 1419 catch( const uno::RuntimeException& ) 1420 { 1421 } 1422 return bMovedOrMoveAllowed; 1423 } 1424 } // anonymous namespace 1425 1426 bool DiagramHelper::isSeriesMoveable( 1427 const Reference< XDiagram >& xDiagram, 1428 const Reference< XDataSeries >& xGivenDataSeries, 1429 bool bForward ) 1430 { 1431 const bool bDoMove = false; 1432 1433 bool bIsMoveable = lcl_moveSeriesOrCheckIfMoveIsAllowed( 1434 xDiagram, xGivenDataSeries, bForward, bDoMove ); 1435 1436 return bIsMoveable; 1437 } 1438 1439 bool DiagramHelper::moveSeries( const Reference< XDiagram >& xDiagram, const Reference< XDataSeries >& xGivenDataSeries, bool bForward ) 1440 { 1441 const bool bDoMove = true; 1442 1443 bool bMoved = lcl_moveSeriesOrCheckIfMoveIsAllowed( 1444 xDiagram, xGivenDataSeries, bForward, bDoMove ); 1445 1446 return bMoved; 1447 } 1448 1449 bool DiagramHelper::isSupportingFloorAndWall( const Reference< 1450 chart2::XDiagram >& xDiagram ) 1451 { 1452 //pies and donuts currently do not support this because of wrong files from older versions 1453 //todo: allow this in future again, if fileversion are available for ole objects (metastream) 1454 //thus the wrong bottom can be removed on import 1455 1456 Sequence< Reference< chart2::XChartType > > aTypes( 1457 ::chart::DiagramHelper::getChartTypesFromDiagram( xDiagram ) ); 1458 for( sal_Int32 nN = 0; nN < aTypes.getLength(); nN++ ) 1459 { 1460 Reference< chart2::XChartType > xType( aTypes[nN] ); 1461 if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) ) 1462 return false; 1463 if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_NET) ) 1464 return false; 1465 if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) ) 1466 return false; 1467 } 1468 return true; 1469 } 1470 1471 bool DiagramHelper::isPieOrDonutChart( const css::uno::Reference< css::chart2::XDiagram >& xDiagram ) 1472 { 1473 uno::Reference< chart2::XChartType > xChartType( DiagramHelper::getChartTypeByIndex( 1474 xDiagram, 0 ) ); 1475 1476 if( xChartType .is() ) 1477 { 1478 OUString aChartType = xChartType->getChartType(); 1479 if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE ) 1480 return true; 1481 } 1482 return false; 1483 } 1484 1485 sal_Int32 DiagramHelper::getGeometry3D( 1486 const uno::Reference< chart2::XDiagram > & xDiagram, 1487 bool& rbFound, bool& rbAmbiguous ) 1488 { 1489 sal_Int32 nCommonGeom( DataPointGeometry3D::CUBOID ); 1490 rbFound = false; 1491 rbAmbiguous = false; 1492 1493 std::vector< Reference< chart2::XDataSeries > > aSeriesVec( 1494 DiagramHelper::getDataSeriesFromDiagram( xDiagram )); 1495 1496 if( aSeriesVec.empty()) 1497 rbAmbiguous = true; 1498 1499 for (auto const& series : aSeriesVec) 1500 { 1501 try 1502 { 1503 sal_Int32 nGeom = 0; 1504 Reference< beans::XPropertySet > xProp(series, uno::UNO_QUERY_THROW); 1505 if( xProp->getPropertyValue( "Geometry3D") >>= nGeom ) 1506 { 1507 if( ! rbFound ) 1508 { 1509 // first series 1510 nCommonGeom = nGeom; 1511 rbFound = true; 1512 } 1513 // further series: compare for uniqueness 1514 else if( nCommonGeom != nGeom ) 1515 { 1516 rbAmbiguous = true; 1517 break; 1518 } 1519 } 1520 } 1521 catch( const uno::Exception & ) 1522 { 1523 DBG_UNHANDLED_EXCEPTION("chart2"); 1524 } 1525 } 1526 1527 return nCommonGeom; 1528 } 1529 1530 void DiagramHelper::setGeometry3D( 1531 const Reference< chart2::XDiagram > & xDiagram, 1532 sal_Int32 nNewGeometry ) 1533 { 1534 std::vector< Reference< chart2::XDataSeries > > aSeriesVec( 1535 DiagramHelper::getDataSeriesFromDiagram( xDiagram )); 1536 1537 for (auto const& series : aSeriesVec) 1538 { 1539 DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( 1540 series, "Geometry3D", uno::Any( nNewGeometry )); 1541 } 1542 } 1543 1544 sal_Int32 DiagramHelper::getCorrectedMissingValueTreatment( 1545 const Reference< chart2::XDiagram > & xDiagram, 1546 const Reference< chart2::XChartType >& xChartType ) 1547 { 1548 sal_Int32 nResult = css::chart::MissingValueTreatment::LEAVE_GAP; 1549 uno::Sequence < sal_Int32 > aAvailableMissingValueTreatments( 1550 ChartTypeHelper::getSupportedMissingValueTreatments( xChartType ) ); 1551 1552 uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY ); 1553 if( xDiaProp.is() && (xDiaProp->getPropertyValue( "MissingValueTreatment" ) >>= nResult) ) 1554 { 1555 //ensure that the set value is supported by this charttype 1556 for( sal_Int32 nN = 0; nN < aAvailableMissingValueTreatments.getLength(); nN++ ) 1557 if( aAvailableMissingValueTreatments[nN] == nResult ) 1558 return nResult; //ok 1559 } 1560 1561 //otherwise use the first supported one 1562 if( aAvailableMissingValueTreatments.getLength() ) 1563 { 1564 nResult = aAvailableMissingValueTreatments[0]; 1565 return nResult; 1566 } 1567 1568 return nResult; 1569 } 1570 1571 DiagramPositioningMode DiagramHelper::getDiagramPositioningMode( const uno::Reference< 1572 chart2::XDiagram > & xDiagram ) 1573 { 1574 DiagramPositioningMode eMode = DiagramPositioningMode_AUTO; 1575 uno::Reference< beans::XPropertySet > xDiaProps( xDiagram, uno::UNO_QUERY ); 1576 if( xDiaProps.is() ) 1577 { 1578 RelativePosition aRelPos; 1579 RelativeSize aRelSize; 1580 if( (xDiaProps->getPropertyValue("RelativePosition") >>= aRelPos ) && 1581 (xDiaProps->getPropertyValue("RelativeSize") >>= aRelSize ) ) 1582 { 1583 bool bPosSizeExcludeAxes=false; 1584 xDiaProps->getPropertyValue("PosSizeExcludeAxes") >>= bPosSizeExcludeAxes; 1585 if( bPosSizeExcludeAxes ) 1586 eMode = DiagramPositioningMode_EXCLUDING; 1587 else 1588 eMode = DiagramPositioningMode_INCLUDING; 1589 } 1590 } 1591 return eMode; 1592 } 1593 1594 static void lcl_ensureRange0to1( double& rValue ) 1595 { 1596 if(rValue<0.0) 1597 rValue=0.0; 1598 if(rValue>1.0) 1599 rValue=1.0; 1600 } 1601 1602 bool DiagramHelper::setDiagramPositioning( const uno::Reference< frame::XModel >& xChartModel, 1603 const awt::Rectangle& rPosRect /*100th mm*/ ) 1604 { 1605 ControllerLockGuardUNO aCtrlLockGuard( xChartModel ); 1606 1607 bool bChanged = false; 1608 awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) ); 1609 uno::Reference< beans::XPropertySet > xDiaProps( ChartModelHelper::findDiagram( xChartModel ), uno::UNO_QUERY ); 1610 if( !xDiaProps.is() ) 1611 return bChanged; 1612 1613 RelativePosition aOldPos; 1614 RelativeSize aOldSize; 1615 xDiaProps->getPropertyValue("RelativePosition" ) >>= aOldPos; 1616 xDiaProps->getPropertyValue("RelativeSize" ) >>= aOldSize; 1617 1618 RelativePosition aNewPos; 1619 aNewPos.Anchor = drawing::Alignment_TOP_LEFT; 1620 aNewPos.Primary = double(rPosRect.X)/double(aPageSize.Width); 1621 aNewPos.Secondary = double(rPosRect.Y)/double(aPageSize.Height); 1622 1623 chart2::RelativeSize aNewSize; 1624 aNewSize.Primary = double(rPosRect.Width)/double(aPageSize.Width); 1625 aNewSize.Secondary = double(rPosRect.Height)/double(aPageSize.Height); 1626 1627 lcl_ensureRange0to1( aNewPos.Primary ); 1628 lcl_ensureRange0to1( aNewPos.Secondary ); 1629 lcl_ensureRange0to1( aNewSize.Primary ); 1630 lcl_ensureRange0to1( aNewSize.Secondary ); 1631 if( (aNewPos.Primary + aNewSize.Primary) > 1.0 ) 1632 aNewPos.Primary = 1.0 - aNewSize.Primary; 1633 if( (aNewPos.Secondary + aNewSize.Secondary) > 1.0 ) 1634 aNewPos.Secondary = 1.0 - aNewSize.Secondary; 1635 1636 xDiaProps->setPropertyValue( "RelativePosition", uno::Any(aNewPos) ); 1637 xDiaProps->setPropertyValue( "RelativeSize", uno::Any(aNewSize) ); 1638 1639 bChanged = (aOldPos.Anchor!=aNewPos.Anchor) || 1640 (aOldPos.Primary!=aNewPos.Primary) || 1641 (aOldPos.Secondary!=aNewPos.Secondary) || 1642 (aOldSize.Primary!=aNewSize.Primary) || 1643 (aOldSize.Secondary!=aNewSize.Secondary); 1644 return bChanged; 1645 } 1646 1647 awt::Rectangle DiagramHelper::getDiagramRectangleFromModel( const uno::Reference< frame::XModel >& xChartModel ) 1648 { 1649 awt::Rectangle aRet(-1,-1,-1,-1); 1650 1651 uno::Reference< beans::XPropertySet > xDiaProps( ChartModelHelper::findDiagram( xChartModel ), uno::UNO_QUERY ); 1652 if( !xDiaProps.is() ) 1653 return aRet; 1654 1655 awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) ); 1656 1657 RelativePosition aRelPos; 1658 RelativeSize aRelSize; 1659 xDiaProps->getPropertyValue("RelativePosition" ) >>= aRelPos; 1660 xDiaProps->getPropertyValue("RelativeSize" ) >>= aRelSize; 1661 1662 awt::Size aAbsSize( 1663 static_cast< sal_Int32 >( aRelSize.Primary * aPageSize.Width ), 1664 static_cast< sal_Int32 >( aRelSize.Secondary * aPageSize.Height )); 1665 1666 awt::Point aAbsPos( 1667 static_cast< sal_Int32 >( aRelPos.Primary * aPageSize.Width ), 1668 static_cast< sal_Int32 >( aRelPos.Secondary * aPageSize.Height )); 1669 1670 awt::Point aAbsPosLeftTop = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject( aAbsPos, aAbsSize, aRelPos.Anchor ); 1671 1672 aRet = awt::Rectangle(aAbsPosLeftTop.X, aAbsPosLeftTop.Y, aAbsSize.Width, aAbsSize.Height ); 1673 1674 return aRet; 1675 } 1676 1677 bool DiagramHelper::switchDiagramPositioningToExcludingPositioning( 1678 ChartModel& rModel, bool bResetModifiedState, bool bConvertAlsoFromAutoPositioning ) 1679 { 1680 //return true if something was changed 1681 const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion( SvtSaveOptions().GetODFDefaultVersion() ); 1682 if( nCurrentODFVersion > SvtSaveOptions::ODFVER_012 ) 1683 { 1684 uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( rModel.getFirstDiagram(), uno::UNO_QUERY ); 1685 if( xDiagramPositioning.is() && ( bConvertAlsoFromAutoPositioning || !xDiagramPositioning->isAutomaticDiagramPositioning() ) 1686 && !xDiagramPositioning->isExcludingDiagramPositioning() ) 1687 { 1688 ControllerLockGuard aCtrlLockGuard( rModel ); 1689 bool bModelWasModified = rModel.isModified(); 1690 xDiagramPositioning->setDiagramPositionExcludingAxes( xDiagramPositioning->calculateDiagramPositionExcludingAxes() ); 1691 if(bResetModifiedState && !bModelWasModified ) 1692 rModel.setModified(false); 1693 return true; 1694 } 1695 } 1696 return false; 1697 } 1698 1699 } // namespace chart 1700 1701 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1702
