1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_chart2.hxx" 30 #include <basegfx/numeric/ftools.hxx> 31 32 #include "VCartesianAxis.hxx" 33 #include "PlottingPositionHelper.hxx" 34 #include "ShapeFactory.hxx" 35 #include "CommonConverters.hxx" 36 #include "macros.hxx" 37 #include "ViewDefines.hxx" 38 #include "PropertyMapper.hxx" 39 #include "NumberFormatterWrapper.hxx" 40 #include "LabelPositionHelper.hxx" 41 #include "TrueGuard.hxx" 42 #include "BaseGFXHelper.hxx" 43 #include "AxisHelper.hxx" 44 #include "Tickmarks_Equidistant.hxx" 45 46 #include <rtl/math.hxx> 47 #include <tools/color.hxx> 48 #include <tools/debug.hxx> 49 #include <com/sun/star/text/XText.hpp> 50 #include <com/sun/star/text/WritingMode2.hpp> 51 #include <editeng/unoprnms.hxx> 52 #include <svx/unoshape.hxx> 53 #include <svx/unoshtxt.hxx> 54 55 #include <algorithm> 56 #include <memory> 57 58 //............................................................................. 59 namespace chart 60 { 61 //............................................................................. 62 using namespace ::com::sun::star; 63 using namespace ::com::sun::star::chart2; 64 using namespace ::rtl::math; 65 using ::com::sun::star::uno::Reference; 66 67 //----------------------------------------------------------------------------- 68 //----------------------------------------------------------------------------- 69 //----------------------------------------------------------------------------- 70 71 VCartesianAxis::VCartesianAxis( const AxisProperties& rAxisProperties 72 , const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier 73 , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount 74 , PlottingPositionHelper* pPosHelper )//takes ownership 75 : VAxisBase( nDimensionIndex, nDimensionCount, rAxisProperties, xNumberFormatsSupplier ) 76 { 77 if( pPosHelper ) 78 m_pPosHelper = pPosHelper; 79 else 80 m_pPosHelper = new PlottingPositionHelper(); 81 } 82 83 VCartesianAxis::~VCartesianAxis() 84 { 85 delete m_pPosHelper; 86 m_pPosHelper = NULL; 87 } 88 89 //----------------------------------------------------------------------------- 90 //----------------------------------------------------------------------------- 91 92 Reference< drawing::XShape > createSingleLabel( 93 const Reference< lang::XMultiServiceFactory>& xShapeFactory 94 , const Reference< drawing::XShapes >& xTarget 95 , const awt::Point& rAnchorScreenPosition2D 96 , const rtl::OUString& rLabel 97 , const AxisLabelProperties& rAxisLabelProperties 98 , const AxisProperties& rAxisProperties 99 , const tNameSequence& rPropNames 100 , const tAnySequence& rPropValues 101 ) 102 { 103 if(!rLabel.getLength()) 104 return 0; 105 106 // #i78696# use mathematically correct rotation now 107 const double fRotationAnglePi(rAxisLabelProperties.fRotationAngleDegree * (F_PI / -180.0)); 108 uno::Any aATransformation = ShapeFactory::makeTransformation( rAnchorScreenPosition2D, fRotationAnglePi ); 109 rtl::OUString aLabel = ShapeFactory::getStackedString( rLabel, rAxisLabelProperties.bStackCharacters ); 110 111 Reference< drawing::XShape > xShape2DText = ShapeFactory(xShapeFactory) 112 .createText( xTarget, aLabel, rPropNames, rPropValues, aATransformation ); 113 114 //correctPositionForRotation 115 LabelPositionHelper::correctPositionForRotation( xShape2DText 116 , rAxisProperties.m_aLabelAlignment, rAxisLabelProperties.fRotationAngleDegree, rAxisProperties.m_bComplexCategories ); 117 118 return xShape2DText; 119 } 120 121 bool lcl_doesShapeOverlapWithTickmark( const Reference< drawing::XShape >& xShape 122 , double fRotationAngleDegree 123 , const basegfx::B2DVector& rTickScreenPosition 124 , bool bIsHorizontalAxis, bool bIsVerticalAxis ) 125 { 126 if(!xShape.is()) 127 return false; 128 129 ::basegfx::B2IRectangle aShapeRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),ShapeFactory::getSizeAfterRotation( xShape, fRotationAngleDegree )); 130 131 if( bIsVerticalAxis ) 132 { 133 return ( (rTickScreenPosition.getY() >= aShapeRect.getMinY()) 134 && (rTickScreenPosition.getY() <= aShapeRect.getMaxY()) ); 135 } 136 if( bIsHorizontalAxis ) 137 { 138 return ( (rTickScreenPosition.getX() >= aShapeRect.getMinX()) 139 && (rTickScreenPosition.getX() <= aShapeRect.getMaxX()) ); 140 } 141 142 basegfx::B2IVector aPosition( 143 static_cast<sal_Int32>( rTickScreenPosition.getX() ) 144 , static_cast<sal_Int32>( rTickScreenPosition.getY() ) ); 145 return aShapeRect.isInside(aPosition); 146 } 147 148 bool doesOverlap( const Reference< drawing::XShape >& xShape1 149 , const Reference< drawing::XShape >& xShape2 150 , double fRotationAngleDegree ) 151 { 152 if( !xShape1.is() || !xShape2.is() ) 153 return false; 154 155 ::basegfx::B2IRectangle aRect1( BaseGFXHelper::makeRectangle(xShape1->getPosition(),ShapeFactory::getSizeAfterRotation( xShape1, fRotationAngleDegree ))); 156 ::basegfx::B2IRectangle aRect2( BaseGFXHelper::makeRectangle(xShape2->getPosition(),ShapeFactory::getSizeAfterRotation( xShape2, fRotationAngleDegree ))); 157 return aRect1.overlaps(aRect2); 158 } 159 160 void removeShapesAtWrongRhythm( TickIter& rIter 161 , sal_Int32 nCorrectRhythm 162 , sal_Int32 nMaxTickToCheck 163 , const Reference< drawing::XShapes >& xTarget ) 164 { 165 sal_Int32 nTick = 0; 166 for( TickInfo* pTickInfo = rIter.firstInfo() 167 ; pTickInfo && nTick <= nMaxTickToCheck 168 ; pTickInfo = rIter.nextInfo(), nTick++ ) 169 { 170 //remove labels which does not fit into the rhythm 171 if( nTick%nCorrectRhythm != 0) 172 { 173 if(pTickInfo->xTextShape.is()) 174 { 175 xTarget->remove(pTickInfo->xTextShape); 176 pTickInfo->xTextShape = NULL; 177 } 178 } 179 } 180 } 181 182 class LabelIterator : public TickIter 183 { 184 //this Iterator iterates over existing text labels 185 186 //if the labels are staggered and bInnerLine is true 187 //we iterate only through the labels which are lying more inside the diagram 188 189 //if the labels are staggered and bInnerLine is false 190 //we iterate only through the labels which are lying more outside the diagram 191 192 //if the labels are not staggered 193 //we iterate through all labels 194 195 public: 196 LabelIterator( ::std::vector< TickInfo >& rTickInfoVector 197 , const AxisLabelStaggering eAxisLabelStaggering 198 , bool bInnerLine ); 199 200 virtual TickInfo* firstInfo(); 201 virtual TickInfo* nextInfo(); 202 203 private: //methods 204 LabelIterator(); 205 206 private: //member 207 PureTickIter m_aPureTickIter; 208 const AxisLabelStaggering m_eAxisLabelStaggering; 209 bool m_bInnerLine; 210 }; 211 212 LabelIterator::LabelIterator( ::std::vector< TickInfo >& rTickInfoVector 213 , const AxisLabelStaggering eAxisLabelStaggering 214 , bool bInnerLine ) 215 : m_aPureTickIter( rTickInfoVector ) 216 , m_eAxisLabelStaggering(eAxisLabelStaggering) 217 , m_bInnerLine(bInnerLine) 218 { 219 } 220 221 TickInfo* LabelIterator::firstInfo() 222 { 223 TickInfo* pTickInfo = m_aPureTickIter.firstInfo(); 224 while( pTickInfo && !pTickInfo->xTextShape.is() ) 225 pTickInfo = m_aPureTickIter.nextInfo(); 226 if(!pTickInfo) 227 return NULL; 228 if( (STAGGER_EVEN==m_eAxisLabelStaggering && m_bInnerLine) 229 || 230 (STAGGER_ODD==m_eAxisLabelStaggering && !m_bInnerLine) 231 ) 232 { 233 //skip first label 234 do 235 pTickInfo = m_aPureTickIter.nextInfo(); 236 while( pTickInfo && !pTickInfo->xTextShape.is() ); 237 } 238 if(!pTickInfo) 239 return NULL; 240 return pTickInfo; 241 } 242 243 TickInfo* LabelIterator::nextInfo() 244 { 245 TickInfo* pTickInfo = NULL; 246 //get next label 247 do 248 pTickInfo = m_aPureTickIter.nextInfo(); 249 while( pTickInfo && !pTickInfo->xTextShape.is() ); 250 251 if( STAGGER_EVEN==m_eAxisLabelStaggering 252 || STAGGER_ODD==m_eAxisLabelStaggering ) 253 { 254 //skip one label 255 do 256 pTickInfo = m_aPureTickIter.nextInfo(); 257 while( pTickInfo && !pTickInfo->xTextShape.is() ); 258 } 259 return pTickInfo; 260 } 261 262 B2DVector lcl_getLabelsDistance( TickIter& rIter, const B2DVector& rDistanceTickToText, double fRotationAngleDegree ) 263 { 264 //calculates the height or width of a line of labels 265 //thus a following line of labels can be shifted for that distance 266 267 B2DVector aRet(0,0); 268 269 sal_Int32 nDistanceTickToText = static_cast<sal_Int32>( rDistanceTickToText.getLength() ); 270 if( nDistanceTickToText==0.0) 271 return aRet; 272 273 B2DVector aStaggerDirection(rDistanceTickToText); 274 aStaggerDirection.normalize(); 275 276 sal_Int32 nDistance=0; 277 Reference< drawing::XShape > xShape2DText(NULL); 278 for( TickInfo* pTickInfo = rIter.firstInfo() 279 ; pTickInfo 280 ; pTickInfo = rIter.nextInfo() ) 281 { 282 xShape2DText = pTickInfo->xTextShape; 283 if( xShape2DText.is() ) 284 { 285 awt::Size aSize = ShapeFactory::getSizeAfterRotation( xShape2DText, fRotationAngleDegree ); 286 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY())) 287 nDistance = ::std::max(nDistance,aSize.Width); 288 else 289 nDistance = ::std::max(nDistance,aSize.Height); 290 } 291 } 292 293 aRet = aStaggerDirection*nDistance; 294 295 //add extra distance for vertical distance 296 if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY())) 297 aRet += rDistanceTickToText; 298 299 return aRet; 300 } 301 302 void lcl_shiftLables( TickIter& rIter, const B2DVector& rStaggerDistance ) 303 { 304 if(rStaggerDistance.getLength()==0.0) 305 return; 306 Reference< drawing::XShape > xShape2DText(NULL); 307 for( TickInfo* pTickInfo = rIter.firstInfo() 308 ; pTickInfo 309 ; pTickInfo = rIter.nextInfo() ) 310 { 311 xShape2DText = pTickInfo->xTextShape; 312 if( xShape2DText.is() ) 313 { 314 awt::Point aPos = xShape2DText->getPosition(); 315 aPos.X += static_cast<sal_Int32>(rStaggerDistance.getX()); 316 aPos.Y += static_cast<sal_Int32>(rStaggerDistance.getY()); 317 xShape2DText->setPosition( aPos ); 318 } 319 } 320 } 321 322 bool lcl_hasWordBreak( const Reference< drawing::XShape >& rxShape ) 323 { 324 if ( rxShape.is() ) 325 { 326 SvxShape* pShape = SvxShape::getImplementation( rxShape ); 327 SvxShapeText* pShapeText = dynamic_cast< SvxShapeText* >( pShape ); 328 if ( pShapeText ) 329 { 330 SvxTextEditSource* pTextEditSource = dynamic_cast< SvxTextEditSource* >( pShapeText->GetEditSource() ); 331 if ( pTextEditSource ) 332 { 333 pTextEditSource->UpdateOutliner(); 334 SvxTextForwarder* pTextForwarder = pTextEditSource->GetTextForwarder(); 335 if ( pTextForwarder ) 336 { 337 sal_uInt16 nParaCount = pTextForwarder->GetParagraphCount(); 338 for ( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara ) 339 { 340 sal_uInt16 nLineCount = pTextForwarder->GetLineCount( nPara ); 341 for ( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine ) 342 { 343 sal_uInt16 nLineStart = 0; 344 sal_uInt16 nLineEnd = 0; 345 pTextForwarder->GetLineBoundaries( nLineStart, nLineEnd, nPara, nLine ); 346 sal_uInt16 nWordStart = 0; 347 sal_uInt16 nWordEnd = 0; 348 if ( pTextForwarder->GetWordIndices( nPara, nLineStart, nWordStart, nWordEnd ) && 349 ( nWordStart != nLineStart ) ) 350 { 351 return true; 352 } 353 } 354 } 355 } 356 } 357 } 358 } 359 360 return false; 361 } 362 363 class MaxLabelTickIter : public TickIter 364 { 365 //iterate over first two and last two labels and the longest label 366 public: 367 MaxLabelTickIter( ::std::vector< TickInfo >& rTickInfoVector 368 , sal_Int32 nLongestLabelIndex ); 369 virtual ~MaxLabelTickIter(); 370 371 virtual TickInfo* firstInfo(); 372 virtual TickInfo* nextInfo(); 373 374 private: 375 ::std::vector< TickInfo >& m_rTickInfoVector; 376 ::std::vector< sal_Int32 > m_aValidIndices; 377 sal_Int32 m_nCurrentIndex; 378 }; 379 380 MaxLabelTickIter::MaxLabelTickIter( ::std::vector< TickInfo >& rTickInfoVector 381 , sal_Int32 nLongestLabelIndex ) 382 : m_rTickInfoVector(rTickInfoVector) 383 , m_nCurrentIndex(0) 384 { 385 sal_Int32 nMaxIndex = m_rTickInfoVector.size()-1; 386 if( nLongestLabelIndex<0 || nLongestLabelIndex>=nMaxIndex-1 ) 387 nLongestLabelIndex = 0; 388 389 if( nMaxIndex>=0 ) 390 m_aValidIndices.push_back(0); 391 if( nMaxIndex>=1 ) 392 m_aValidIndices.push_back(1); 393 if( nLongestLabelIndex>1 ) 394 m_aValidIndices.push_back(nLongestLabelIndex); 395 if( nMaxIndex > 2 ) 396 m_aValidIndices.push_back(nMaxIndex-1); 397 if( nMaxIndex > 1 ) 398 m_aValidIndices.push_back(nMaxIndex); 399 } 400 MaxLabelTickIter::~MaxLabelTickIter() 401 { 402 } 403 404 TickInfo* MaxLabelTickIter::firstInfo() 405 { 406 m_nCurrentIndex = 0; 407 if( m_nCurrentIndex < static_cast<sal_Int32>(m_aValidIndices.size()) ) 408 return &m_rTickInfoVector[m_aValidIndices[m_nCurrentIndex]]; 409 return 0; 410 } 411 412 TickInfo* MaxLabelTickIter::nextInfo() 413 { 414 m_nCurrentIndex++; 415 if( m_nCurrentIndex>=0 && m_nCurrentIndex<static_cast<sal_Int32>(m_aValidIndices.size()) ) 416 return &m_rTickInfoVector[m_aValidIndices[m_nCurrentIndex]]; 417 return 0; 418 } 419 420 bool VCartesianAxis::isBreakOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties 421 , bool bIsHorizontalAxis ) 422 { 423 if( m_aTextLabels.getLength() > 100 ) 424 return false; 425 if( !rAxisLabelProperties.bLineBreakAllowed ) 426 return false; 427 if( rAxisLabelProperties.bStackCharacters ) 428 return false; 429 //no break for value axis 430 if( !m_bUseTextLabels ) 431 return false; 432 if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) ) 433 return false; 434 //break only for horizontal axis 435 return bIsHorizontalAxis; 436 } 437 438 bool VCartesianAxis::isAutoStaggeringOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties 439 , bool bIsHorizontalAxis, bool bIsVerticalAxis ) 440 { 441 if( rAxisLabelProperties.eStaggering != STAGGER_AUTO ) 442 return false; 443 if( rAxisLabelProperties.bOverlapAllowed ) 444 return false; 445 if( rAxisLabelProperties.bLineBreakAllowed ) //auto line break or auto staggering, doing both automatisms they may conflict... 446 return false; 447 if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) ) 448 return false; 449 //automatic staggering only for horizontal axis with horizontal text 450 //or vertical axis with vertical text 451 if( bIsHorizontalAxis ) 452 return !rAxisLabelProperties.bStackCharacters; 453 if( bIsVerticalAxis ) 454 return rAxisLabelProperties.bStackCharacters; 455 return false; 456 } 457 458 struct ComplexCategoryPlacement 459 { 460 rtl::OUString Text; 461 sal_Int32 Count; 462 double TickValue; 463 464 ComplexCategoryPlacement( const rtl::OUString& rText, sal_Int32 nCount, double fTickValue ) 465 : Text(rText), Count(nCount), TickValue(fTickValue) 466 {} 467 }; 468 469 void VCartesianAxis::createAllTickInfosFromComplexCategories( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos, bool bShiftedPosition ) 470 { 471 //no minor tickmarks will be generated! 472 //order is: inner labels first , outer labels last (that is different to all other TickIter cases) 473 if(!bShiftedPosition) 474 { 475 rAllTickInfos.clear(); 476 sal_Int32 nLevel=0; 477 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); 478 for( ; nLevel<nLevelCount; nLevel++ ) 479 { 480 ::std::vector< TickInfo > aTickInfoVector; 481 std::vector< ComplexCategory > aComplexCategories( m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel( nLevel ) ); 482 sal_Int32 nCatIndex = 0; 483 std::vector< ComplexCategory >::const_iterator aIt(aComplexCategories.begin()); 484 std::vector< ComplexCategory >::const_iterator aEnd(aComplexCategories.end()); 485 for(;aIt!=aEnd;++aIt) 486 { 487 TickInfo aTickInfo(0); 488 ComplexCategory aCat(*aIt); 489 sal_Int32 nCount = aCat.Count; 490 if( nCatIndex + 1.0 + nCount >= m_aScale.Maximum ) 491 { 492 nCount = static_cast<sal_Int32>(m_aScale.Maximum - 1.0 - nCatIndex); 493 if( nCount <= 0 ) 494 nCount = 1; 495 } 496 aTickInfo.fScaledTickValue = nCatIndex + 1.0 + nCount/2.0; 497 aTickInfo.nFactorForLimitedTextWidth = nCount; 498 aTickInfo.aText = aCat.Text; 499 aTickInfoVector.push_back(aTickInfo); 500 nCatIndex += nCount; 501 if( nCatIndex + 1.0 >= m_aScale.Maximum ) 502 break; 503 } 504 rAllTickInfos.push_back(aTickInfoVector); 505 } 506 } 507 else //bShiftedPosition==false 508 { 509 rAllTickInfos.clear(); 510 sal_Int32 nLevel=0; 511 sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); 512 for( ; nLevel<nLevelCount; nLevel++ ) 513 { 514 ::std::vector< TickInfo > aTickInfoVector; 515 std::vector< ComplexCategory > aComplexCategories( m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel( nLevel ) ); 516 sal_Int32 nCatIndex = 0; 517 std::vector< ComplexCategory >::const_iterator aIt(aComplexCategories.begin()); 518 std::vector< ComplexCategory >::const_iterator aEnd(aComplexCategories.end()); 519 for(;aIt!=aEnd;++aIt) 520 { 521 TickInfo aTickInfo(0); 522 ComplexCategory aCat(*aIt); 523 aTickInfo.fScaledTickValue = nCatIndex + 1.0; 524 aTickInfoVector.push_back(aTickInfo); 525 nCatIndex += aCat.Count; 526 if( nCatIndex + 1.0 > m_aScale.Maximum ) 527 break; 528 } 529 //fill up with single ticks until maximum scale 530 while( nCatIndex + 1.0 < m_aScale.Maximum ) 531 { 532 TickInfo aTickInfo(0); 533 aTickInfo.fScaledTickValue = nCatIndex + 1.0; 534 aTickInfoVector.push_back(aTickInfo); 535 nCatIndex ++; 536 if( nLevel>0 ) 537 break; 538 } 539 //add an additional tick at the end 540 { 541 TickInfo aTickInfo(0); 542 aTickInfo.fScaledTickValue = m_aScale.Maximum; 543 aTickInfoVector.push_back(aTickInfo); 544 } 545 rAllTickInfos.push_back(aTickInfoVector); 546 } 547 } 548 } 549 550 void VCartesianAxis::createAllTickInfos( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) 551 { 552 if( isComplexCategoryAxis() ) 553 createAllTickInfosFromComplexCategories( rAllTickInfos, false ); 554 else 555 VAxisBase::createAllTickInfos(rAllTickInfos); 556 } 557 558 ::std::auto_ptr< TickIter > VCartesianAxis::createLabelTickIterator( sal_Int32 nTextLevel ) 559 { 560 if( nTextLevel>=0 && nTextLevel < static_cast< sal_Int32 >(m_aAllTickInfos.size()) ) 561 return ::std::auto_ptr< TickIter >( new PureTickIter( m_aAllTickInfos[nTextLevel] ) ); 562 return ::std::auto_ptr< TickIter >(); 563 } 564 ::std::auto_ptr< TickIter > VCartesianAxis::createMaximumLabelTickIterator( sal_Int32 nTextLevel ) 565 { 566 if( isComplexCategoryAxis() || isDateAxis() ) 567 { 568 return createLabelTickIterator( nTextLevel ); //mmmm maybe todo: create less than all texts here 569 } 570 else 571 { 572 if(nTextLevel==0) 573 { 574 if( !m_aAllTickInfos.empty() ) 575 { 576 sal_Int32 nLongestLabelIndex = m_bUseTextLabels ? this->getIndexOfLongestLabel( m_aTextLabels ) : 0; 577 return ::std::auto_ptr< TickIter >( new MaxLabelTickIter( m_aAllTickInfos[0], nLongestLabelIndex ) ); 578 } 579 } 580 } 581 return ::std::auto_ptr< TickIter >(); 582 } 583 584 sal_Int32 VCartesianAxis::getTextLevelCount() const 585 { 586 sal_Int32 nTextLevelCount = 1; 587 if( isComplexCategoryAxis() ) 588 nTextLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); 589 return nTextLevelCount; 590 } 591 592 bool VCartesianAxis::createTextShapes( 593 const Reference< drawing::XShapes >& xTarget 594 , TickIter& rTickIter 595 , AxisLabelProperties& rAxisLabelProperties 596 , TickFactory_2D* pTickFactory 597 , sal_Int32 nScreenDistanceBetweenTicks ) 598 { 599 //returns true if the text shapes have been created succesfully 600 //otherwise false - in this case the AxisLabelProperties have changed 601 //and contain new instructions for the next try for text shape creation 602 603 Reference< XScaling > xInverseScaling( NULL ); 604 if( m_aScale.Scaling.is() ) 605 xInverseScaling = m_aScale.Scaling->getInverseScaling(); 606 607 FixedNumberFormatter aFixedNumberFormatter( 608 m_xNumberFormatsSupplier, rAxisLabelProperties.nNumberFormatKey ); 609 610 const bool bIsHorizontalAxis = pTickFactory->isHorizontalAxis(); 611 const bool bIsVerticalAxis = pTickFactory->isVerticalAxis(); 612 bool bIsStaggered = rAxisLabelProperties.getIsStaggered(); 613 B2DVector aTextToTickDistance( pTickFactory->getDistanceAxisTickToText( m_aAxisProperties, true ) ); 614 sal_Int32 nLimitedSpaceForText = -1; 615 if( isBreakOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis ) ) 616 { 617 nLimitedSpaceForText = nScreenDistanceBetweenTicks; 618 if( bIsStaggered ) 619 nLimitedSpaceForText *= 2; 620 621 if( nLimitedSpaceForText > 0 ) 622 { //reduce space for a small amount to have a visible distance between the labels: 623 sal_Int32 nReduce = (nLimitedSpaceForText*5)/100; 624 if(!nReduce) 625 nReduce = 1; 626 nLimitedSpaceForText -= nReduce; 627 } 628 } 629 630 std::vector< ComplexCategoryPlacement > aComplexCategoryPlacements; 631 uno::Sequence< rtl::OUString >* pCategories = 0; 632 if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories ) 633 pCategories = &m_aTextLabels; 634 635 TickInfo* pPreviousVisibleTickInfo = NULL; 636 TickInfo* pPREPreviousVisibleTickInfo = NULL; 637 TickInfo* pLastVisibleNeighbourTickInfo = NULL; 638 639 //------------------------------------------------ 640 //prepare properties for multipropertyset-interface of shape 641 tNameSequence aPropNames; 642 tAnySequence aPropValues; 643 644 bool bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY()); 645 Reference< beans::XPropertySet > xProps( m_aAxisProperties.m_xAxisModel, uno::UNO_QUERY ); 646 PropertyMapper::getTextLabelMultiPropertyLists( xProps, aPropNames, aPropValues, false 647 , nLimitedSpaceForText, bLimitedHeight ); 648 LabelPositionHelper::doDynamicFontResize( aPropValues, aPropNames, xProps 649 , m_aAxisLabelProperties.m_aFontReferenceSize ); 650 LabelPositionHelper::changeTextAdjustment( aPropValues, aPropNames, m_aAxisProperties.m_aLabelAlignment ); 651 652 uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,C2U("CharColor")); 653 sal_Int32 nColor = Color( COL_AUTO ).GetColor(); 654 if(pColorAny) 655 *pColorAny >>= nColor; 656 657 uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight); 658 //------------------------------------------------ 659 660 sal_Int32 nTick = 0; 661 for( TickInfo* pTickInfo = rTickIter.firstInfo() 662 ; pTickInfo 663 ; pTickInfo = rTickIter.nextInfo(), nTick++ ) 664 { 665 pLastVisibleNeighbourTickInfo = bIsStaggered ? 666 pPREPreviousVisibleTickInfo : pPreviousVisibleTickInfo; 667 668 //don't create labels which does not fit into the rhythm 669 if( nTick%rAxisLabelProperties.nRhythm != 0) 670 continue; 671 672 //don't create labels for invisible ticks 673 if( !pTickInfo->bPaintIt ) 674 continue; 675 676 //if NO OVERLAP -> don't create labels where the tick overlaps 677 //with the text of the last neighbour tickmark 678 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed ) 679 { 680 if( lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape 681 , rAxisLabelProperties.fRotationAngleDegree 682 , pTickInfo->aTickScreenPosition 683 , bIsHorizontalAxis, bIsVerticalAxis ) ) 684 { 685 bool bOverlapAlsoAfterSwitchingOnAutoStaggering = true; 686 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) ) 687 { 688 bIsStaggered = true; 689 rAxisLabelProperties.eStaggering = STAGGER_EVEN; 690 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo; 691 if( !pLastVisibleNeighbourTickInfo || 692 !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape 693 , rAxisLabelProperties.fRotationAngleDegree 694 , pTickInfo->aTickScreenPosition 695 , bIsHorizontalAxis, bIsVerticalAxis ) ) 696 bOverlapAlsoAfterSwitchingOnAutoStaggering = false; 697 } 698 if( bOverlapAlsoAfterSwitchingOnAutoStaggering ) 699 { 700 if( rAxisLabelProperties.bRhythmIsFix ) 701 continue; 702 rAxisLabelProperties.nRhythm++; 703 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget ); 704 return false; 705 } 706 } 707 } 708 709 //xxxxx pTickInfo->updateUnscaledValue( xInverseScaling ); 710 711 bool bHasExtraColor=false; 712 sal_Int32 nExtraColor=0; 713 714 rtl::OUString aLabel; 715 if(pCategories) 716 { 717 sal_Int32 nIndex = static_cast< sal_Int32 >(pTickInfo->getUnscaledTickValue()) - 1; //first category (index 0) matches with real number 1.0 718 if( nIndex>=0 && nIndex<pCategories->getLength() ) 719 aLabel = (*pCategories)[nIndex]; 720 } 721 else if( m_aAxisProperties.m_bComplexCategories ) 722 { 723 aLabel = pTickInfo->aText; 724 } 725 else 726 aLabel = aFixedNumberFormatter.getFormattedString( pTickInfo->getUnscaledTickValue(), nExtraColor, bHasExtraColor ); 727 728 if(pColorAny) 729 *pColorAny = uno::makeAny(bHasExtraColor?nExtraColor:nColor); 730 if(pLimitedSpaceAny) 731 *pLimitedSpaceAny = uno::makeAny(sal_Int32(nLimitedSpaceForText*pTickInfo->nFactorForLimitedTextWidth)); 732 733 B2DVector aTickScreenPos2D( pTickInfo->aTickScreenPosition ); 734 aTickScreenPos2D += aTextToTickDistance; 735 awt::Point aAnchorScreenPosition2D( 736 static_cast<sal_Int32>(aTickScreenPos2D.getX()) 737 ,static_cast<sal_Int32>(aTickScreenPos2D.getY())); 738 739 //create single label 740 if(!pTickInfo->xTextShape.is()) 741 pTickInfo->xTextShape = createSingleLabel( m_xShapeFactory, xTarget 742 , aAnchorScreenPosition2D, aLabel 743 , rAxisLabelProperties, m_aAxisProperties 744 , aPropNames, aPropValues ); 745 if(!pTickInfo->xTextShape.is()) 746 continue; 747 748 recordMaximumTextSize( pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ); 749 750 //better rotate if single words are broken apart 751 if( nLimitedSpaceForText>0 && !rAxisLabelProperties.bOverlapAllowed 752 && ::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) 753 && m_aAxisProperties.m_bComplexCategories 754 && lcl_hasWordBreak( pTickInfo->xTextShape ) ) 755 { 756 rAxisLabelProperties.fRotationAngleDegree = 90; 757 rAxisLabelProperties.bLineBreakAllowed = false; 758 m_aAxisLabelProperties.fRotationAngleDegree = rAxisLabelProperties.fRotationAngleDegree; 759 removeTextShapesFromTicks(); 760 return false; 761 } 762 763 //if NO OVERLAP -> remove overlapping shapes 764 if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed ) 765 { 766 if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ) ) 767 { 768 bool bOverlapAlsoAfterSwitchingOnAutoStaggering = true; 769 if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) ) 770 { 771 bIsStaggered = true; 772 rAxisLabelProperties.eStaggering = STAGGER_EVEN; 773 pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo; 774 if( !pLastVisibleNeighbourTickInfo || 775 !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape 776 , rAxisLabelProperties.fRotationAngleDegree 777 , pTickInfo->aTickScreenPosition 778 , bIsHorizontalAxis, bIsVerticalAxis ) ) 779 bOverlapAlsoAfterSwitchingOnAutoStaggering = false; 780 } 781 if( bOverlapAlsoAfterSwitchingOnAutoStaggering ) 782 { 783 if( rAxisLabelProperties.bRhythmIsFix ) 784 { 785 xTarget->remove(pTickInfo->xTextShape); 786 pTickInfo->xTextShape = NULL; 787 continue; 788 } 789 rAxisLabelProperties.nRhythm++; 790 removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget ); 791 return false; 792 } 793 } 794 } 795 796 pPREPreviousVisibleTickInfo = pPreviousVisibleTickInfo; 797 pPreviousVisibleTickInfo = pTickInfo; 798 } 799 return true; 800 } 801 802 drawing::PointSequenceSequence lcl_makePointSequence( B2DVector& rStart, B2DVector& rEnd ) 803 { 804 drawing::PointSequenceSequence aPoints(1); 805 aPoints[0].realloc(2); 806 aPoints[0][0].X = static_cast<sal_Int32>(rStart.getX()); 807 aPoints[0][0].Y = static_cast<sal_Int32>(rStart.getY()); 808 aPoints[0][1].X = static_cast<sal_Int32>(rEnd.getX()); 809 aPoints[0][1].Y = static_cast<sal_Int32>(rEnd.getY()); 810 return aPoints; 811 } 812 813 double VCartesianAxis::getLogicValueWhereMainLineCrossesOtherAxis() const 814 { 815 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); 816 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); 817 818 double fCrossesOtherAxis; 819 if(m_aAxisProperties.m_pfMainLinePositionAtOtherAxis) 820 fCrossesOtherAxis = *m_aAxisProperties.m_pfMainLinePositionAtOtherAxis; 821 else 822 { 823 if( ::com::sun::star::chart::ChartAxisPosition_END == m_aAxisProperties.m_eCrossoverType ) 824 fCrossesOtherAxis = fMax; 825 else 826 fCrossesOtherAxis = fMin; 827 } 828 return fCrossesOtherAxis; 829 } 830 831 double VCartesianAxis::getLogicValueWhereLabelLineCrossesOtherAxis() const 832 { 833 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); 834 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); 835 836 double fCrossesOtherAxis; 837 if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == m_aAxisProperties.m_eLabelPos ) 838 fCrossesOtherAxis = fMin; 839 else if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == m_aAxisProperties.m_eLabelPos ) 840 fCrossesOtherAxis = fMax; 841 else 842 fCrossesOtherAxis = getLogicValueWhereMainLineCrossesOtherAxis(); 843 return fCrossesOtherAxis; 844 } 845 846 bool VCartesianAxis::getLogicValueWhereExtraLineCrossesOtherAxis( double& fCrossesOtherAxis ) const 847 { 848 if( !m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis ) 849 return false; 850 double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); 851 double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); 852 if( *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis <= fMin 853 || *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis >= fMax ) 854 return false; 855 fCrossesOtherAxis = *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis; 856 return true; 857 } 858 859 B2DVector VCartesianAxis::getScreenPosition( double fLogicX, double fLogicY, double fLogicZ ) const 860 { 861 B2DVector aRet(0,0); 862 863 if( m_pPosHelper ) 864 { 865 drawing::Position3D aScenePos = m_pPosHelper->transformLogicToScene( fLogicX, fLogicY, fLogicZ, true ); 866 if(3==m_nDimension) 867 { 868 if( m_xLogicTarget.is() && m_pPosHelper && m_pShapeFactory ) 869 { 870 tPropertyNameMap aDummyPropertyNameMap; 871 Reference< drawing::XShape > xShape3DAnchor = m_pShapeFactory->createCube( m_xLogicTarget 872 , aScenePos,drawing::Direction3D(1,1,1), 0, 0, aDummyPropertyNameMap); 873 awt::Point a2DPos = xShape3DAnchor->getPosition(); //get 2D position from xShape3DAnchor 874 m_xLogicTarget->remove(xShape3DAnchor); 875 aRet.setX( a2DPos.X ); 876 aRet.setY( a2DPos.Y ); 877 } 878 else 879 { 880 DBG_ERROR("cannot calculate scrren position in VCartesianAxis::getScreenPosition"); 881 } 882 } 883 else 884 { 885 aRet.setX( aScenePos.PositionX ); 886 aRet.setY( aScenePos.PositionY ); 887 } 888 } 889 890 return aRet; 891 } 892 893 VCartesianAxis::ScreenPosAndLogicPos VCartesianAxis::getScreenPosAndLogicPos( double fLogicX_, double fLogicY_, double fLogicZ_ ) const 894 { 895 ScreenPosAndLogicPos aRet; 896 aRet.fLogicX = fLogicX_; 897 aRet.fLogicY = fLogicY_; 898 aRet.fLogicZ = fLogicZ_; 899 aRet.aScreenPos = getScreenPosition( fLogicX_, fLogicY_, fLogicZ_ ); 900 return aRet; 901 } 902 903 typedef ::std::vector< VCartesianAxis::ScreenPosAndLogicPos > tScreenPosAndLogicPosList; 904 struct lcl_LessXPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool > 905 { 906 inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 ) 907 { 908 return ( rPos1.aScreenPos.getX() < rPos2.aScreenPos.getX() ); 909 } 910 }; 911 912 struct lcl_GreaterYPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool > 913 { 914 inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 ) 915 { 916 return ( rPos1.aScreenPos.getY() > rPos2.aScreenPos.getY() ); 917 } 918 }; 919 920 void VCartesianAxis::get2DAxisMainLine( B2DVector& rStart, B2DVector& rEnd, double fCrossesOtherAxis ) 921 { 922 //m_aAxisProperties might get updated and changed here because 923 // the label alignmant and inner direction sign depends exactly of the choice of the axis line position which is made here in this method 924 925 double fMinX = m_pPosHelper->getLogicMinX(); 926 double fMinY = m_pPosHelper->getLogicMinY(); 927 double fMinZ = m_pPosHelper->getLogicMinZ(); 928 double fMaxX = m_pPosHelper->getLogicMaxX(); 929 double fMaxY = m_pPosHelper->getLogicMaxY(); 930 double fMaxZ = m_pPosHelper->getLogicMaxZ(); 931 932 double fXStart = fMinX; 933 double fYStart = fMinY; 934 double fZStart = fMinZ; 935 double fXEnd = fXStart; 936 double fYEnd = fYStart; 937 double fZEnd = fZStart; 938 939 double fXOnXPlane = fMinX; 940 double fXOther = fMaxX; 941 int nDifferentValue = !m_pPosHelper->isMathematicalOrientationX() ? -1 : 1; 942 if( !m_pPosHelper->isSwapXAndY() ) 943 nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1; 944 else 945 nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1; 946 if( nDifferentValue<0 ) 947 { 948 fXOnXPlane = fMaxX; 949 fXOther = fMinX; 950 } 951 952 double fYOnYPlane = fMinY; 953 double fYOther = fMaxY; 954 nDifferentValue = !m_pPosHelper->isMathematicalOrientationY() ? -1 : 1; 955 if( !m_pPosHelper->isSwapXAndY() ) 956 nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1; 957 else 958 nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1; 959 if( nDifferentValue<0 ) 960 { 961 fYOnYPlane = fMaxY; 962 fYOther = fMinY; 963 } 964 965 double fZOnZPlane = fMaxZ; 966 double fZOther = fMinZ; 967 nDifferentValue = !m_pPosHelper->isMathematicalOrientationZ() ? -1 : 1; 968 nDifferentValue *= (CuboidPlanePosition_Back != m_eBackWallPos) ? -1 : 1; 969 if( nDifferentValue<0 ) 970 { 971 fZOnZPlane = fMinZ; 972 fZOther = fMaxZ; 973 } 974 975 if( 0==m_nDimensionIndex ) //x-axis 976 { 977 if( fCrossesOtherAxis < fMinY ) 978 fCrossesOtherAxis = fMinY; 979 else if( fCrossesOtherAxis > fMaxY ) 980 fCrossesOtherAxis = fMaxY; 981 982 fYStart = fYEnd = fCrossesOtherAxis; 983 fXEnd=m_pPosHelper->getLogicMaxX(); 984 985 if(3==m_nDimension) 986 { 987 if( AxisHelper::isAxisPositioningEnabled() ) 988 { 989 if( ::rtl::math::approxEqual( fYOther, fYStart) ) 990 fZStart = fZEnd = fZOnZPlane; 991 else 992 fZStart = fZEnd = fZOther; 993 } 994 else 995 { 996 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 997 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 998 999 double fDeltaX = rEnd.getX() - rStart.getX(); 1000 double fDeltaY = rEnd.getY() - rStart.getY(); 1001 1002 //only those points are candidates which are lying on exactly one wall as these are outer edges 1003 tScreenPosAndLogicPosList aPosList; 1004 aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOnYPlane, fZOther ) ); 1005 aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOther, fZOnZPlane ) ); 1006 1007 if( fabs(fDeltaY) > fabs(fDeltaX) ) 1008 { 1009 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; 1010 //choose most left positions 1011 ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() ); 1012 m_aAxisProperties.m_fLabelDirectionSign = fDeltaY<0 ? -1 : 1; 1013 } 1014 else 1015 { 1016 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; 1017 //choose most bottom positions 1018 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); 1019 m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; 1020 } 1021 ScreenPosAndLogicPos aBestPos( aPosList[0] ); 1022 fYStart = fYEnd = aBestPos.fLogicY; 1023 fZStart = fZEnd = aBestPos.fLogicZ; 1024 if( !m_pPosHelper->isMathematicalOrientationX() ) 1025 m_aAxisProperties.m_fLabelDirectionSign *= -1; 1026 } 1027 }//end 3D x axis 1028 } 1029 else if( 1==m_nDimensionIndex ) //y-axis 1030 { 1031 if( fCrossesOtherAxis < fMinX ) 1032 fCrossesOtherAxis = fMinX; 1033 else if( fCrossesOtherAxis > fMaxX ) 1034 fCrossesOtherAxis = fMaxX; 1035 1036 fXStart = fXEnd = fCrossesOtherAxis; 1037 fYEnd=m_pPosHelper->getLogicMaxY(); 1038 1039 if(3==m_nDimension) 1040 { 1041 if( AxisHelper::isAxisPositioningEnabled() ) 1042 { 1043 if( ::rtl::math::approxEqual( fXOther, fXStart) ) 1044 fZStart = fZEnd = fZOnZPlane; 1045 else 1046 fZStart = fZEnd = fZOther; 1047 } 1048 else 1049 { 1050 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 1051 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 1052 1053 double fDeltaX = rEnd.getX() - rStart.getX(); 1054 double fDeltaY = rEnd.getY() - rStart.getY(); 1055 1056 //only those points are candidates which are lying on exactly one wall as these are outer edges 1057 tScreenPosAndLogicPosList aPosList; 1058 aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fMinY, fZOther ) ); 1059 aPosList.push_back( getScreenPosAndLogicPos( fXOther, fMinY, fZOnZPlane ) ); 1060 1061 if( fabs(fDeltaY) > fabs(fDeltaX) ) 1062 { 1063 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; 1064 //choose most left positions 1065 ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() ); 1066 m_aAxisProperties.m_fLabelDirectionSign = fDeltaY<0 ? -1 : 1; 1067 } 1068 else 1069 { 1070 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; 1071 //choose most bottom positions 1072 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); 1073 m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; 1074 } 1075 ScreenPosAndLogicPos aBestPos( aPosList[0] ); 1076 fXStart = fXEnd = aBestPos.fLogicX; 1077 fZStart = fZEnd = aBestPos.fLogicZ; 1078 if( !m_pPosHelper->isMathematicalOrientationY() ) 1079 m_aAxisProperties.m_fLabelDirectionSign *= -1; 1080 } 1081 }//end 3D y axis 1082 } 1083 else //z-axis 1084 { 1085 fZEnd = m_pPosHelper->getLogicMaxZ(); 1086 if( AxisHelper::isAxisPositioningEnabled() ) 1087 { 1088 if( !m_aAxisProperties.m_bSwapXAndY ) 1089 { 1090 if( fCrossesOtherAxis < fMinY ) 1091 fCrossesOtherAxis = fMinY; 1092 else if( fCrossesOtherAxis > fMaxY ) 1093 fCrossesOtherAxis = fMaxY; 1094 fYStart = fYEnd = fCrossesOtherAxis; 1095 1096 if( ::rtl::math::approxEqual( fYOther, fYStart) ) 1097 fXStart = fXEnd = fXOnXPlane; 1098 else 1099 fXStart = fXEnd = fXOther; 1100 } 1101 else 1102 { 1103 if( fCrossesOtherAxis < fMinX ) 1104 fCrossesOtherAxis = fMinX; 1105 else if( fCrossesOtherAxis > fMaxX ) 1106 fCrossesOtherAxis = fMaxX; 1107 fXStart = fXEnd = fCrossesOtherAxis; 1108 1109 if( ::rtl::math::approxEqual( fXOther, fXStart) ) 1110 fYStart = fYEnd = fYOnYPlane; 1111 else 1112 fYStart = fYEnd = fYOther; 1113 } 1114 } 1115 else 1116 { 1117 if( !m_pPosHelper->isSwapXAndY() ) 1118 { 1119 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMinX(); 1120 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMinY() : m_pPosHelper->getLogicMaxY(); 1121 } 1122 else 1123 { 1124 fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMaxX(); 1125 fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMaxY() : m_pPosHelper->getLogicMinY(); 1126 } 1127 1128 if(3==m_nDimension) 1129 { 1130 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 1131 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 1132 1133 double fDeltaX = rEnd.getX() - rStart.getX(); 1134 1135 //only those points are candidates which are lying on exactly one wall as these are outer edges 1136 tScreenPosAndLogicPosList aPosList; 1137 aPosList.push_back( getScreenPosAndLogicPos( fXOther, fYOnYPlane, fMinZ ) ); 1138 aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fYOther, fMinZ ) ); 1139 1140 ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); 1141 ScreenPosAndLogicPos aBestPos( aPosList[0] ); 1142 ScreenPosAndLogicPos aNotSoGoodPos( aPosList[1] ); 1143 1144 //choose most bottom positions 1145 if( !::rtl::math::approxEqual( fDeltaX, 0.0 ) ) // prefere left-right algnments 1146 { 1147 if( aBestPos.aScreenPos.getX() > aNotSoGoodPos.aScreenPos.getX() ) 1148 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_RIGHT; 1149 else 1150 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; 1151 } 1152 else 1153 { 1154 if( aBestPos.aScreenPos.getY() > aNotSoGoodPos.aScreenPos.getY() ) 1155 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; 1156 else 1157 m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_TOP; 1158 } 1159 1160 m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; 1161 if( !m_pPosHelper->isMathematicalOrientationZ() ) 1162 m_aAxisProperties.m_fLabelDirectionSign *= -1; 1163 1164 fXStart = fXEnd = aBestPos.fLogicX; 1165 fYStart = fYEnd = aBestPos.fLogicY; 1166 } 1167 }//end 3D z axis 1168 } 1169 1170 rStart = getScreenPosition( fXStart, fYStart, fZStart ); 1171 rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); 1172 1173 if(3==m_nDimension && !AxisHelper::isAxisPositioningEnabled() ) 1174 m_aAxisProperties.m_fInnerDirectionSign = m_aAxisProperties.m_fLabelDirectionSign;//to behave like before 1175 1176 if(3==m_nDimension && AxisHelper::isAxisPositioningEnabled() ) 1177 { 1178 double fDeltaX = rEnd.getX() - rStart.getX(); 1179 double fDeltaY = rEnd.getY() - rStart.getY(); 1180 1181 if( 2==m_nDimensionIndex ) 1182 { 1183 if( m_eLeftWallPos != CuboidPlanePosition_Left ) 1184 { 1185 m_aAxisProperties.m_fLabelDirectionSign *= -1.0; 1186 m_aAxisProperties.m_fInnerDirectionSign *= -1.0; 1187 } 1188 1189 m_aAxisProperties.m_aLabelAlignment = 1190 ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? 1191 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1192 1193 if( ( fDeltaY<0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || 1194 ( fDeltaY>0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) 1195 m_aAxisProperties.m_aLabelAlignment = 1196 ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_RIGHT ) ? 1197 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1198 } 1199 else if( fabs(fDeltaY) > fabs(fDeltaX) ) 1200 { 1201 if( m_eBackWallPos != CuboidPlanePosition_Back ) 1202 { 1203 m_aAxisProperties.m_fLabelDirectionSign *= -1.0; 1204 m_aAxisProperties.m_fInnerDirectionSign *= -1.0; 1205 } 1206 1207 m_aAxisProperties.m_aLabelAlignment = 1208 ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? 1209 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1210 1211 if( ( fDeltaY<0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || 1212 ( fDeltaY>0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) 1213 m_aAxisProperties.m_aLabelAlignment = 1214 ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_RIGHT ) ? 1215 LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; 1216 } 1217 else 1218 { 1219 if( m_eBackWallPos != CuboidPlanePosition_Back ) 1220 { 1221 m_aAxisProperties.m_fLabelDirectionSign *= -1.0; 1222 m_aAxisProperties.m_fInnerDirectionSign *= -1.0; 1223 } 1224 1225 m_aAxisProperties.m_aLabelAlignment = 1226 ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? 1227 LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM; 1228 1229 if( ( fDeltaX>0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || 1230 ( fDeltaX<0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) 1231 m_aAxisProperties.m_aLabelAlignment = 1232 ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_TOP ) ? 1233 LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP; 1234 } 1235 } 1236 } 1237 1238 TickFactory* VCartesianAxis::createTickFactory() 1239 { 1240 return createTickFactory2D(); 1241 } 1242 1243 TickFactory_2D* VCartesianAxis::createTickFactory2D() 1244 { 1245 B2DVector aStart, aEnd; 1246 this->get2DAxisMainLine( aStart, aEnd, this->getLogicValueWhereMainLineCrossesOtherAxis() ); 1247 1248 B2DVector aLabelLineStart, aLabelLineEnd; 1249 this->get2DAxisMainLine( aLabelLineStart, aLabelLineEnd, this->getLogicValueWhereLabelLineCrossesOtherAxis() ); 1250 1251 return new TickFactory_2D( m_aScale, m_aIncrement, aStart, aEnd, aLabelLineStart-aStart ); 1252 } 1253 1254 void lcl_hideIdenticalScreenValues( TickIter& rTickIter ) 1255 { 1256 TickInfo* pPreviousTickInfo = rTickIter.firstInfo(); 1257 if(!pPreviousTickInfo) 1258 return; 1259 pPreviousTickInfo->bPaintIt = true; 1260 for( TickInfo* pTickInfo = rTickIter.nextInfo(); pTickInfo; pTickInfo = rTickIter.nextInfo()) 1261 { 1262 pTickInfo->bPaintIt = 1263 ( static_cast<sal_Int32>(pTickInfo->aTickScreenPosition.getX()) 1264 != static_cast<sal_Int32>(pPreviousTickInfo->aTickScreenPosition.getX()) ) 1265 || 1266 ( static_cast<sal_Int32>(pTickInfo->aTickScreenPosition.getY()) 1267 != static_cast<sal_Int32>(pPreviousTickInfo->aTickScreenPosition.getY()) ); 1268 pPreviousTickInfo = pTickInfo; 1269 } 1270 } 1271 1272 //'hide' tickmarks with identical screen values in aAllTickInfos 1273 void VCartesianAxis::hideIdenticalScreenValues( ::std::vector< ::std::vector< TickInfo > >& rTickInfos ) const 1274 { 1275 if( isComplexCategoryAxis() || isDateAxis() ) 1276 { 1277 sal_Int32 nCount = rTickInfos.size(); 1278 for( sal_Int32 nN=0; nN<nCount; nN++ ) 1279 { 1280 PureTickIter aTickIter( rTickInfos[nN] ); 1281 lcl_hideIdenticalScreenValues( aTickIter ); 1282 } 1283 } 1284 else 1285 { 1286 EquidistantTickIter aTickIter( rTickInfos, m_aIncrement, 0, -1 ); 1287 lcl_hideIdenticalScreenValues( aTickIter ); 1288 } 1289 } 1290 1291 sal_Int32 VCartesianAxis::estimateMaximumAutoMainIncrementCount() 1292 { 1293 sal_Int32 nRet = 10; 1294 1295 if( m_nMaximumTextWidthSoFar==0 && m_nMaximumTextHeightSoFar==0 ) 1296 return nRet; 1297 1298 B2DVector aStart, aEnd; 1299 this->get2DAxisMainLine( aStart, aEnd, this->getLogicValueWhereMainLineCrossesOtherAxis() ); 1300 1301 sal_Int32 nMaxHeight = static_cast<sal_Int32>(fabs(aEnd.getY()-aStart.getY())); 1302 sal_Int32 nMaxWidth = static_cast<sal_Int32>(fabs(aEnd.getX()-aStart.getX())); 1303 1304 sal_Int32 nTotalAvailable = nMaxHeight; 1305 sal_Int32 nSingleNeeded = m_nMaximumTextHeightSoFar; 1306 1307 //for horizontal axis: 1308 if( (m_nDimensionIndex == 0 && !m_aAxisProperties.m_bSwapXAndY) 1309 || (m_nDimensionIndex == 1 && m_aAxisProperties.m_bSwapXAndY) ) 1310 { 1311 nTotalAvailable = nMaxWidth; 1312 nSingleNeeded = m_nMaximumTextWidthSoFar; 1313 } 1314 1315 if( nSingleNeeded>0 ) 1316 nRet = nTotalAvailable/nSingleNeeded; 1317 1318 return nRet; 1319 } 1320 1321 void VCartesianAxis::doStaggeringOfLabels( const AxisLabelProperties& rAxisLabelProperties, TickFactory_2D* pTickFactory2D ) 1322 { 1323 if( !pTickFactory2D ) 1324 return; 1325 1326 if( isComplexCategoryAxis() ) 1327 { 1328 sal_Int32 nTextLevelCount = getTextLevelCount(); 1329 B2DVector aCummulatedLabelsDistance(0,0); 1330 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1331 { 1332 ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); 1333 if(apTickIter.get()) 1334 { 1335 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree; 1336 if( nTextLevel>0 ) 1337 { 1338 lcl_shiftLables( *apTickIter.get(), aCummulatedLabelsDistance ); 1339 fRotationAngleDegree = 0.0; 1340 } 1341 aCummulatedLabelsDistance += lcl_getLabelsDistance( *apTickIter.get() 1342 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties ) 1343 , fRotationAngleDegree ); 1344 } 1345 } 1346 } 1347 else if( rAxisLabelProperties.getIsStaggered() ) 1348 { 1349 if( !m_aAllTickInfos.empty() ) 1350 { 1351 LabelIterator aInnerIter( m_aAllTickInfos[0], rAxisLabelProperties.eStaggering, true ); 1352 LabelIterator aOuterIter( m_aAllTickInfos[0], rAxisLabelProperties.eStaggering, false ); 1353 1354 lcl_shiftLables( aOuterIter 1355 , lcl_getLabelsDistance( aInnerIter 1356 , pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties ), 0.0 ) ); 1357 } 1358 } 1359 } 1360 1361 void VCartesianAxis::createLabels() 1362 { 1363 if( !prepareShapeCreation() ) 1364 return; 1365 1366 //----------------------------------------- 1367 //create labels 1368 if( m_aAxisProperties.m_bDisplayLabels ) 1369 { 1370 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1371 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1372 if( !pTickFactory2D ) 1373 return; 1374 1375 //----------------------------------------- 1376 //get the transformed screen values for all tickmarks in aAllTickInfos 1377 pTickFactory2D->updateScreenValues( m_aAllTickInfos ); 1378 //----------------------------------------- 1379 //'hide' tickmarks with identical screen values in aAllTickInfos 1380 hideIdenticalScreenValues( m_aAllTickInfos ); 1381 1382 removeTextShapesFromTicks(); 1383 1384 //create tick mark text shapes 1385 sal_Int32 nTextLevelCount = getTextLevelCount(); 1386 sal_Int32 nScreenDistanceBetweenTicks = -1; 1387 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1388 { 1389 ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); 1390 if(apTickIter.get()) 1391 { 1392 if(nTextLevel==0) 1393 { 1394 nScreenDistanceBetweenTicks = TickFactory_2D::getTickScreenDistance( *apTickIter.get() ); 1395 if( nTextLevelCount>1 ) 1396 nScreenDistanceBetweenTicks*=2; //the above used tick iter does contain also the sub ticks -> thus the given distance is only the half 1397 } 1398 1399 AxisLabelProperties aComplexProps(m_aAxisLabelProperties); 1400 if( m_aAxisProperties.m_bComplexCategories ) 1401 { 1402 if( nTextLevel==0 ) 1403 { 1404 aComplexProps.bLineBreakAllowed = true; 1405 aComplexProps.bOverlapAllowed = !::rtl::math::approxEqual( aComplexProps.fRotationAngleDegree, 0.0 ); 1406 } 1407 else 1408 { 1409 aComplexProps.bOverlapAllowed = true; 1410 aComplexProps.bRhythmIsFix = true; 1411 aComplexProps.nRhythm = 1; 1412 aComplexProps.fRotationAngleDegree = 0.0; 1413 } 1414 } 1415 AxisLabelProperties& rAxisLabelProperties = m_aAxisProperties.m_bComplexCategories ? aComplexProps : m_aAxisLabelProperties; 1416 while( !createTextShapes( m_xTextTarget, *apTickIter.get(), rAxisLabelProperties, pTickFactory2D, nScreenDistanceBetweenTicks ) ) 1417 { 1418 }; 1419 } 1420 } 1421 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D ); 1422 } 1423 } 1424 1425 void VCartesianAxis::createMaximumLabels() 1426 { 1427 TrueGuard aRecordMaximumTextSize(m_bRecordMaximumTextSize); 1428 1429 if( !prepareShapeCreation() ) 1430 return; 1431 1432 //----------------------------------------- 1433 //create labels 1434 if( m_aAxisProperties.m_bDisplayLabels ) 1435 { 1436 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1437 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1438 if( !pTickFactory2D ) 1439 return; 1440 1441 //----------------------------------------- 1442 //get the transformed screen values for all tickmarks in aAllTickInfos 1443 pTickFactory2D->updateScreenValues( m_aAllTickInfos ); 1444 1445 //create tick mark text shapes 1446 //@todo: iterate through all tick depth wich should be labeled 1447 1448 AxisLabelProperties aAxisLabelProperties( m_aAxisLabelProperties ); 1449 if( isAutoStaggeringOfLabelsAllowed( aAxisLabelProperties, pTickFactory2D->isHorizontalAxis(), pTickFactory2D->isVerticalAxis() ) ) 1450 aAxisLabelProperties.eStaggering = STAGGER_EVEN; 1451 aAxisLabelProperties.bOverlapAllowed = true; 1452 aAxisLabelProperties.bLineBreakAllowed = false; 1453 sal_Int32 nTextLevelCount = getTextLevelCount(); 1454 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1455 { 1456 ::std::auto_ptr< TickIter > apTickIter = createMaximumLabelTickIterator( nTextLevel ); 1457 if(apTickIter.get()) 1458 { 1459 while( !createTextShapes( m_xTextTarget, *apTickIter.get(), aAxisLabelProperties, pTickFactory2D, -1 ) ) 1460 { 1461 }; 1462 } 1463 } 1464 doStaggeringOfLabels( aAxisLabelProperties, pTickFactory2D ); 1465 } 1466 } 1467 1468 void VCartesianAxis::updatePositions() 1469 { 1470 //----------------------------------------- 1471 //update positions of labels 1472 if( m_aAxisProperties.m_bDisplayLabels ) 1473 { 1474 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1475 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1476 if( !pTickFactory2D ) 1477 return; 1478 1479 //----------------------------------------- 1480 //update positions of all existing text shapes 1481 pTickFactory2D->updateScreenValues( m_aAllTickInfos ); 1482 1483 ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = m_aAllTickInfos.begin(); 1484 const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = m_aAllTickInfos.end(); 1485 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd; aDepthIter++, nDepth++ ) 1486 { 1487 ::std::vector< TickInfo >::iterator aTickIter = aDepthIter->begin(); 1488 const ::std::vector< TickInfo >::const_iterator aTickEnd = aDepthIter->end(); 1489 for( ; aTickIter != aTickEnd; aTickIter++ ) 1490 { 1491 TickInfo& rTickInfo = (*aTickIter); 1492 Reference< drawing::XShape > xShape2DText( rTickInfo.xTextShape ); 1493 if( xShape2DText.is() ) 1494 { 1495 B2DVector aTextToTickDistance( pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, true ) ); 1496 B2DVector aTickScreenPos2D( rTickInfo.aTickScreenPosition ); 1497 aTickScreenPos2D += aTextToTickDistance; 1498 awt::Point aAnchorScreenPosition2D( 1499 static_cast<sal_Int32>(aTickScreenPos2D.getX()) 1500 ,static_cast<sal_Int32>(aTickScreenPos2D.getY())); 1501 1502 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree; 1503 if( nDepth>0 ) 1504 fRotationAngleDegree = 0.0; 1505 1506 // #i78696# use mathematically correct rotation now 1507 const double fRotationAnglePi(fRotationAngleDegree * (F_PI / -180.0)); 1508 uno::Any aATransformation = ShapeFactory::makeTransformation(aAnchorScreenPosition2D, fRotationAnglePi); 1509 1510 //set new position 1511 uno::Reference< beans::XPropertySet > xProp( xShape2DText, uno::UNO_QUERY ); 1512 if( xProp.is() ) 1513 { 1514 try 1515 { 1516 xProp->setPropertyValue( C2U( "Transformation" ), aATransformation ); 1517 } 1518 catch( uno::Exception& e ) 1519 { 1520 ASSERT_EXCEPTION( e ); 1521 } 1522 } 1523 1524 //correctPositionForRotation 1525 LabelPositionHelper::correctPositionForRotation( xShape2DText 1526 , m_aAxisProperties.m_aLabelAlignment, fRotationAngleDegree, m_aAxisProperties.m_bComplexCategories ); 1527 } 1528 } 1529 } 1530 1531 doStaggeringOfLabels( m_aAxisLabelProperties, pTickFactory2D ); 1532 } 1533 } 1534 1535 void VCartesianAxis::createTickMarkLineShapes( ::std::vector< TickInfo >& rTickInfos, const TickmarkProperties& rTickmarkProperties, TickFactory_2D& rTickFactory2D, bool bOnlyAtLabels ) 1536 { 1537 sal_Int32 nPointCount = rTickInfos.size(); 1538 drawing::PointSequenceSequence aPoints(2*nPointCount); 1539 1540 ::std::vector< TickInfo >::const_iterator aTickIter = rTickInfos.begin(); 1541 const ::std::vector< TickInfo >::const_iterator aTickEnd = rTickInfos.end(); 1542 sal_Int32 nN = 0; 1543 for( ; aTickIter != aTickEnd; aTickIter++ ) 1544 { 1545 if( !(*aTickIter).bPaintIt ) 1546 continue; 1547 1548 bool bTicksAtLabels = ( m_aAxisProperties.m_eTickmarkPos != ::com::sun::star::chart::ChartAxisMarkPosition_AT_AXIS ); 1549 double fInnerDirectionSign = m_aAxisProperties.m_fInnerDirectionSign; 1550 if( bTicksAtLabels && m_aAxisProperties.m_eLabelPos == ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END ) 1551 fInnerDirectionSign *= -1.0; 1552 bTicksAtLabels = bTicksAtLabels || bOnlyAtLabels; 1553 //add ticks at labels: 1554 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue 1555 , fInnerDirectionSign , rTickmarkProperties, bTicksAtLabels ); 1556 //add ticks at axis (without lables): 1557 if( !bOnlyAtLabels && m_aAxisProperties.m_eTickmarkPos == ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS ) 1558 rTickFactory2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue 1559 , m_aAxisProperties.m_fInnerDirectionSign, rTickmarkProperties, !bTicksAtLabels ); 1560 } 1561 aPoints.realloc(nN); 1562 m_pShapeFactory->createLine2D( m_xGroupShape_Shapes, aPoints 1563 , &rTickmarkProperties.aLineProperties ); 1564 } 1565 1566 void VCartesianAxis::createShapes() 1567 { 1568 if( !prepareShapeCreation() ) 1569 return; 1570 1571 std::auto_ptr< TickFactory_2D > apTickFactory2D( this->createTickFactory2D() ); 1572 TickFactory_2D* pTickFactory2D = apTickFactory2D.get(); 1573 if( !pTickFactory2D ) 1574 return; 1575 1576 //----------------------------------------- 1577 //create line shapes 1578 if(2==m_nDimension) 1579 { 1580 //----------------------------------------- 1581 //create extra long ticks to separate complex categories (create them only there where the labels are) 1582 if( isComplexCategoryAxis() ) 1583 { 1584 ::std::vector< ::std::vector< TickInfo > > aComplexTickInfos; 1585 createAllTickInfosFromComplexCategories( aComplexTickInfos, true ); 1586 pTickFactory2D->updateScreenValues( aComplexTickInfos ); 1587 hideIdenticalScreenValues( aComplexTickInfos ); 1588 1589 ::std::vector<TickmarkProperties> aTickmarkPropertiesList; 1590 static bool bIncludeSpaceBetweenTickAndText = false; 1591 sal_Int32 nOffset = static_cast<sal_Int32>(pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false, bIncludeSpaceBetweenTickAndText ).getLength()); 1592 sal_Int32 nTextLevelCount = getTextLevelCount(); 1593 for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) 1594 { 1595 ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); 1596 if( apTickIter.get() ) 1597 { 1598 double fRotationAngleDegree = m_aAxisLabelProperties.fRotationAngleDegree; 1599 if( nTextLevel>0 ) 1600 fRotationAngleDegree = 0.0; 1601 B2DVector aLabelsDistance( lcl_getLabelsDistance( *apTickIter.get(), pTickFactory2D->getDistanceAxisTickToText( m_aAxisProperties, false ), fRotationAngleDegree ) ); 1602 sal_Int32 nCurrentLength = static_cast<sal_Int32>(aLabelsDistance.getLength()); 1603 aTickmarkPropertiesList.push_back( m_aAxisProperties.makeTickmarkPropertiesForComplexCategories( nOffset + nCurrentLength, 0, nTextLevel ) ); 1604 nOffset += nCurrentLength; 1605 } 1606 } 1607 1608 sal_Int32 nTickmarkPropertiesCount = aTickmarkPropertiesList.size(); 1609 ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = aComplexTickInfos.begin(); 1610 const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = aComplexTickInfos.end(); 1611 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; aDepthIter++, nDepth++ ) 1612 { 1613 if(nDepth==0 && !m_aAxisProperties.m_nMajorTickmarks) 1614 continue; 1615 createTickMarkLineShapes( *aDepthIter, aTickmarkPropertiesList[nDepth], *pTickFactory2D, true /*bOnlyAtLabels*/ ); 1616 } 1617 } 1618 //----------------------------------------- 1619 //create normal ticks for major and minor intervals 1620 { 1621 ::std::vector< ::std::vector< TickInfo > > aUnshiftedTickInfos; 1622 if( m_aScale.ShiftedCategoryPosition )// if ShiftedCategoryPosition==true the tickmarks in m_aAllTickInfos are shifted 1623 { 1624 pTickFactory2D->getAllTicks( aUnshiftedTickInfos ); 1625 pTickFactory2D->updateScreenValues( aUnshiftedTickInfos ); 1626 hideIdenticalScreenValues( aUnshiftedTickInfos ); 1627 } 1628 ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos = m_aScale.ShiftedCategoryPosition ? aUnshiftedTickInfos : m_aAllTickInfos; 1629 1630 ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = rAllTickInfos.begin(); 1631 const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = rAllTickInfos.end(); 1632 if(aDepthIter == aDepthEnd)//no tickmarks at all 1633 return; 1634 1635 sal_Int32 nTickmarkPropertiesCount = m_aAxisProperties.m_aTickmarkPropertiesList.size(); 1636 for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; aDepthIter++, nDepth++ ) 1637 createTickMarkLineShapes( *aDepthIter, m_aAxisProperties.m_aTickmarkPropertiesList[nDepth], *pTickFactory2D, false /*bOnlyAtLabels*/ ); 1638 } 1639 //----------------------------------------- 1640 //create axis main lines 1641 //it serves also as the handle shape for the axis selection 1642 { 1643 drawing::PointSequenceSequence aPoints(1); 1644 apTickFactory2D->createPointSequenceForAxisMainLine( aPoints ); 1645 Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( 1646 m_xGroupShape_Shapes, aPoints 1647 , &m_aAxisProperties.m_aLineProperties ); 1648 //because of this name this line will be used for marking the axis 1649 m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); 1650 } 1651 //----------------------------------------- 1652 //create an additional line at NULL 1653 if( !AxisHelper::isAxisPositioningEnabled() ) 1654 { 1655 double fExtraLineCrossesOtherAxis; 1656 if( getLogicValueWhereExtraLineCrossesOtherAxis(fExtraLineCrossesOtherAxis) ) 1657 { 1658 B2DVector aStart, aEnd; 1659 this->get2DAxisMainLine( aStart, aEnd, fExtraLineCrossesOtherAxis ); 1660 drawing::PointSequenceSequence aPoints( lcl_makePointSequence(aStart,aEnd) ); 1661 Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( 1662 m_xGroupShape_Shapes, aPoints, &m_aAxisProperties.m_aLineProperties ); 1663 } 1664 } 1665 } 1666 1667 //createLabels(); 1668 } 1669 1670 //............................................................................. 1671 } //namespace chart 1672 //............................................................................. 1673