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