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 
31 #include "LabelPositionHelper.hxx"
32 #include "PlottingPositionHelper.hxx"
33 #include "CommonConverters.hxx"
34 #include "PropertyMapper.hxx"
35 #include "ShapeFactory.hxx"
36 #include "macros.hxx"
37 #include "RelativeSizeHelper.hxx"
38 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
39 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
40 
41 //.............................................................................
42 namespace chart
43 {
44 //.............................................................................
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::chart2;
47 
48 LabelPositionHelper::LabelPositionHelper(
49                     PlottingPositionHelper* pPosHelper
50                     , sal_Int32 nDimensionCount
51                     , const uno::Reference< drawing::XShapes >& xLogicTarget
52                     , ShapeFactory* pShapeFactory )
53                     : m_pPosHelper(pPosHelper)
54                     , m_nDimensionCount(nDimensionCount)
55                     , m_xLogicTarget(xLogicTarget)
56                     , m_pShapeFactory(pShapeFactory)
57 {
58 }
59 
60 LabelPositionHelper::~LabelPositionHelper()
61 {
62 }
63 
64 awt::Point LabelPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D ) const
65 {
66     return PlottingPositionHelper::transformSceneToScreenPosition(
67                   rScenePosition3D, m_xLogicTarget, m_pShapeFactory, m_nDimensionCount );
68 }
69 
70 void LabelPositionHelper::changeTextAdjustment( tAnySequence& rPropValues, const tNameSequence& rPropNames, LabelAlignment eAlignment)
71 {
72     //HorizontalAdjustment
73     {
74         drawing::TextHorizontalAdjust eHorizontalAdjust = drawing::TextHorizontalAdjust_CENTER;
75         if( LABEL_ALIGN_RIGHT==eAlignment || LABEL_ALIGN_RIGHT_TOP==eAlignment || LABEL_ALIGN_RIGHT_BOTTOM==eAlignment )
76             eHorizontalAdjust = drawing::TextHorizontalAdjust_LEFT;
77         else if( LABEL_ALIGN_LEFT==eAlignment || LABEL_ALIGN_LEFT_TOP==eAlignment || LABEL_ALIGN_LEFT_BOTTOM==eAlignment )
78             eHorizontalAdjust = drawing::TextHorizontalAdjust_RIGHT;
79         uno::Any* pHorizontalAdjustAny = PropertyMapper::getValuePointer(rPropValues,rPropNames,C2U("TextHorizontalAdjust"));
80         if(pHorizontalAdjustAny)
81             *pHorizontalAdjustAny = uno::makeAny(eHorizontalAdjust);
82     }
83 
84     //VerticalAdjustment
85     {
86         drawing::TextVerticalAdjust eVerticalAdjust = drawing::TextVerticalAdjust_CENTER;
87         if( LABEL_ALIGN_TOP==eAlignment || LABEL_ALIGN_RIGHT_TOP==eAlignment || LABEL_ALIGN_LEFT_TOP==eAlignment )
88             eVerticalAdjust = drawing::TextVerticalAdjust_BOTTOM;
89         else if( LABEL_ALIGN_BOTTOM==eAlignment || LABEL_ALIGN_RIGHT_BOTTOM==eAlignment || LABEL_ALIGN_LEFT_BOTTOM==eAlignment )
90             eVerticalAdjust = drawing::TextVerticalAdjust_TOP;
91         uno::Any* pVerticalAdjustAny = PropertyMapper::getValuePointer(rPropValues,rPropNames,C2U("TextVerticalAdjust"));
92         if(pVerticalAdjustAny)
93             *pVerticalAdjustAny = uno::makeAny(eVerticalAdjust);
94     }
95 }
96 
97 void lcl_doDynamicFontResize( uno::Any* pAOldAndNewFontHeightAny
98                           , const awt::Size& rOldReferenceSize
99                           , const awt::Size& rNewReferenceSize  )
100 {
101     double fOldFontHeight = 0, fNewFontHeight;
102     if( pAOldAndNewFontHeightAny && ( *pAOldAndNewFontHeightAny >>= fOldFontHeight ) )
103     {
104         fNewFontHeight = RelativeSizeHelper::calculate( fOldFontHeight, rOldReferenceSize, rNewReferenceSize );
105         *pAOldAndNewFontHeightAny = uno::makeAny(fNewFontHeight);
106     }
107 }
108 
109 void LabelPositionHelper::doDynamicFontResize( tAnySequence& rPropValues
110                     , const tNameSequence& rPropNames
111                     , const uno::Reference< beans::XPropertySet >& xAxisModelProps
112                     , const awt::Size& rNewReferenceSize
113                     )
114 {
115     //-------------------------
116     //handle dynamic font resize:
117     awt::Size aOldReferenceSize;
118     if( xAxisModelProps->getPropertyValue( C2U("ReferencePageSize")) >>= aOldReferenceSize )
119     {
120         uno::Any* pAOldAndNewFontHeightAny = PropertyMapper::getValuePointer( rPropValues, rPropNames, C2U("CharHeight") );
121         lcl_doDynamicFontResize( pAOldAndNewFontHeightAny, aOldReferenceSize, rNewReferenceSize );
122         pAOldAndNewFontHeightAny = PropertyMapper::getValuePointer( rPropValues, rPropNames, C2U("CharHeightAsian") );
123         lcl_doDynamicFontResize( pAOldAndNewFontHeightAny, aOldReferenceSize, rNewReferenceSize );
124         pAOldAndNewFontHeightAny = PropertyMapper::getValuePointer( rPropValues, rPropNames, C2U("CharHeightComplex") );
125         lcl_doDynamicFontResize( pAOldAndNewFontHeightAny, aOldReferenceSize, rNewReferenceSize );
126     }
127 }
128 
129 namespace
130 {
131 
132 void lcl_correctRotation_Left( double& rfXCorrection, double& rfYCorrection
133                            , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
134 {
135     //correct label positions for labels on a left side of something with a right centered alignment
136     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
137     if( fAnglePositiveDegree==0.0 )
138     {
139     }
140     else if( fAnglePositiveDegree<= 90.0 )
141     {
142         rfXCorrection = -aSize.Height*rtl::math::sin( fAnglePi )/2.0;
143         if( bRotateAroundCenter )
144             rfYCorrection = -aSize.Width*rtl::math::sin( fAnglePi )/2.0;
145     }
146     else if( fAnglePositiveDegree<= 180.0 )
147     {
148         double beta = fAnglePi-F_PI/2.0;
149         rfXCorrection = -aSize.Width *rtl::math::sin( beta )
150             -aSize.Height *rtl::math::cos( beta )/2.0;
151         if( bRotateAroundCenter )
152             rfYCorrection = -aSize.Width *rtl::math::cos( beta )/2.0;
153         else
154             rfYCorrection = -aSize.Width *rtl::math::cos( beta );
155     }
156     else if( fAnglePositiveDegree<= 270.0 )
157     {
158         double beta = fAnglePi - F_PI;
159         rfXCorrection = -aSize.Width *rtl::math::cos( beta )
160             -aSize.Height*rtl::math::sin( beta )/2.0;
161         if( bRotateAroundCenter )
162             rfYCorrection = aSize.Width *rtl::math::sin( beta )/2.0;
163         else
164             rfYCorrection = aSize.Width *rtl::math::sin( beta );
165     }
166     else
167     {
168         double beta = 2*F_PI - fAnglePi;
169         rfXCorrection = -aSize.Height*rtl::math::sin( beta )/2.0;
170         if( bRotateAroundCenter )
171             rfYCorrection = aSize.Width*rtl::math::sin( beta )/2.0;
172     }
173 }
174 
175 void lcl_correctRotation_Right( double& rfXCorrection, double& rfYCorrection
176                            , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
177 {
178     //correct label positions for labels on a right side of something with a left centered alignment
179     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
180     if( fAnglePositiveDegree== 0.0 )
181     {
182     }
183     else if( fAnglePositiveDegree<= 90.0 )
184     {
185         rfXCorrection = aSize.Height*rtl::math::sin( fAnglePi )/2.0;
186         if( bRotateAroundCenter )
187             rfYCorrection = aSize.Width*rtl::math::sin( fAnglePi )/2.0;
188     }
189     else if( fAnglePositiveDegree<= 180.0 )
190     {
191         double beta = F_PI - fAnglePi;
192         rfXCorrection = aSize.Width *rtl::math::cos( beta )
193             + aSize.Height*rtl::math::sin( beta )/2.0;
194         if( bRotateAroundCenter )
195             rfYCorrection = aSize.Width *rtl::math::sin( beta )/2.0;
196         else
197             rfYCorrection = aSize.Width *rtl::math::sin( beta );
198     }
199     else if( fAnglePositiveDegree<= 270.0 )
200     {
201         double beta = 3*F_PI/2.0 - fAnglePi;
202         rfXCorrection = aSize.Width *rtl::math::sin( beta )
203                     +aSize.Height*rtl::math::cos( beta )/2.0;
204         if( bRotateAroundCenter )
205             rfYCorrection = -aSize.Width *rtl::math::cos( beta )/2.0;
206         else
207             rfYCorrection = -aSize.Width *rtl::math::cos( beta );
208     }
209     else
210     {
211         rfXCorrection  = aSize.Height*rtl::math::sin( 2*F_PI - fAnglePi )/2.0;
212         if( bRotateAroundCenter )
213             rfYCorrection = -aSize.Width*rtl::math::sin( 2*F_PI - fAnglePi )/2.0;
214     }
215 }
216 
217 void lcl_correctRotation_Top( double& rfXCorrection, double& rfYCorrection
218                            , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
219 {
220     //correct label positions for labels on top of something with a bottom centered alignment
221     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
222     if( fAnglePositiveDegree== 0.0 )
223     {
224     }
225     else if( fAnglePositiveDegree<= 90.0 )
226     {
227         rfXCorrection = aSize.Height*rtl::math::sin( fAnglePi )/2.0;
228         if( !bRotateAroundCenter )
229             rfXCorrection += aSize.Width*rtl::math::cos( fAnglePi )/2.0;
230         rfYCorrection = -aSize.Width*rtl::math::sin( fAnglePi )/2.0;
231     }
232     else if( fAnglePositiveDegree<= 180.0 )
233     {
234         double beta = fAnglePi - F_PI/2.0;
235         rfXCorrection = aSize.Height*rtl::math::cos( beta )/2.0;
236         if( !bRotateAroundCenter )
237             rfXCorrection -= aSize.Width*rtl::math::sin( beta )/2.0;
238         rfYCorrection = -aSize.Width*rtl::math::cos( beta )/2.0
239             - aSize.Height*rtl::math::sin( beta );
240     }
241     else if( fAnglePositiveDegree<= 270.0 )
242     {
243         double beta = fAnglePi - F_PI;
244         rfXCorrection = -aSize.Height *rtl::math::sin( beta )/2.0;
245         if( !bRotateAroundCenter )
246             rfXCorrection += aSize.Width *rtl::math::cos( beta )/2.0;
247         rfYCorrection = -aSize.Width *rtl::math::sin( beta )/2.0
248             -aSize.Height *rtl::math::cos( beta );
249     }
250     else
251     {
252         rfXCorrection = aSize.Height*rtl::math::sin( fAnglePi )/2.0;
253         if( !bRotateAroundCenter )
254             rfXCorrection -= aSize.Width*rtl::math::cos( fAnglePi )/2.0;
255         rfYCorrection = aSize.Width*rtl::math::sin( fAnglePi )/2.0;
256     }
257 }
258 
259 void lcl_correctRotation_Bottom( double& rfXCorrection, double& rfYCorrection
260                            , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
261 {
262     //correct label positions for labels below something with a top centered alignment
263     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
264     if( fAnglePositiveDegree==0.0 )
265     {
266     }
267     else if( fAnglePositiveDegree<= 90.0 )
268     {
269         rfXCorrection = -aSize.Height*rtl::math::sin( fAnglePi )/2.0;
270         if( !bRotateAroundCenter )
271             rfXCorrection -= aSize.Width *rtl::math::cos( fAnglePi )/2.0;
272         rfYCorrection = aSize.Width*rtl::math::sin( fAnglePi )/2.0;
273     }
274     else if( fAnglePositiveDegree<= 180.0 )
275     {
276         double beta = fAnglePi-F_PI/2.0;
277         rfXCorrection = -aSize.Height*rtl::math::cos( beta )/2.0;
278         if( !bRotateAroundCenter )
279             rfXCorrection += aSize.Width *rtl::math::sin( beta )/2.0;
280         rfYCorrection = aSize.Width *rtl::math::cos( beta )/2.0
281             +aSize.Height*rtl::math::sin( beta );
282     }
283     else if( fAnglePositiveDegree<= 270.0 )
284     {
285         double beta = 3*F_PI/2.0 - fAnglePi;
286         rfXCorrection = aSize.Height*rtl::math::cos( beta )/2.0;
287         if( !bRotateAroundCenter )
288             rfXCorrection -= aSize.Width *rtl::math::sin( beta )/2.0;
289         rfYCorrection = aSize.Height*rtl::math::sin( beta )
290                         +aSize.Width*rtl::math::cos( beta )/2.0;
291     }
292     else
293     {
294         double beta = 2*F_PI - fAnglePi;
295         rfXCorrection = aSize.Height*rtl::math::sin( beta )/2.0;
296         if( !bRotateAroundCenter )
297             rfXCorrection += aSize.Width*rtl::math::cos( beta )/2.0;
298         rfYCorrection = aSize.Width*rtl::math::sin( beta )/2.0;
299     }
300 }
301 
302 void lcl_correctRotation_Left_Top( double& rfXCorrection, double& rfYCorrection
303                            , double fAnglePositiveDegree, const awt::Size& aSize )
304 {
305     //correct position for labels at the left top corner of something with a bottom right alignment
306     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
307     if( fAnglePositiveDegree==0.0 )
308     {
309     }
310     else if( fAnglePositiveDegree<= 90.0 )
311     {
312         rfYCorrection = -aSize.Width*rtl::math::sin( fAnglePi );
313     }
314     else if( fAnglePositiveDegree<= 180.0 )
315     {
316         double beta = fAnglePi-F_PI/2.0;
317         rfXCorrection = -aSize.Width*rtl::math::sin( beta );
318         rfYCorrection = -aSize.Height*rtl::math::sin( beta )
319                         -aSize.Width*rtl::math::cos( beta );
320     }
321     else if( fAnglePositiveDegree<= 270.0 )
322     {
323         double beta = 3*F_PI/2.0 - fAnglePi;
324         rfXCorrection = -aSize.Height*rtl::math::cos( beta )
325                         -aSize.Width*rtl::math::sin( beta );
326         rfYCorrection = -aSize.Height*rtl::math::sin( beta );
327     }
328     else
329     {
330         rfXCorrection = aSize.Height*rtl::math::sin( fAnglePi );
331     }
332 }
333 
334 void lcl_correctRotation_Left_Bottom( double& rfXCorrection, double& rfYCorrection
335                            , double fAnglePositiveDegree, const awt::Size& aSize )
336 {
337     //correct position for labels at the left bottom corner of something with a top right alignment
338     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
339     if( fAnglePositiveDegree==0.0 )
340     {
341     }
342     else if( fAnglePositiveDegree<= 90.0 )
343     {
344         rfXCorrection = -aSize.Height*rtl::math::sin( fAnglePi );
345     }
346     else if( fAnglePositiveDegree<= 180.0 )
347     {
348         double beta = fAnglePi-F_PI/2.0;
349         rfXCorrection = -aSize.Width*rtl::math::sin( beta )
350                         -aSize.Height*rtl::math::cos( beta );;
351         rfYCorrection = aSize.Height*rtl::math::sin( beta );
352     }
353     else if( fAnglePositiveDegree<= 270.0 )
354     {
355         double beta = 3*F_PI/2.0 - fAnglePi;
356         rfXCorrection = -aSize.Width*rtl::math::sin( beta );
357         rfYCorrection = aSize.Width*rtl::math::cos( beta )
358                         +aSize.Height*rtl::math::sin( beta );
359     }
360     else
361     {
362         rfYCorrection = -aSize.Width*rtl::math::sin( fAnglePi );
363     }
364 }
365 
366 void lcl_correctRotation_Right_Top( double& rfXCorrection, double& rfYCorrection
367                            , double fAnglePositiveDegree, const awt::Size& aSize )
368 {
369     //correct position for labels at the right top corner of something with a bottom left alignment
370     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
371     if( fAnglePositiveDegree==0.0 )
372     {
373     }
374     else if( fAnglePositiveDegree<= 90.0 )
375     {
376         rfXCorrection = aSize.Height*rtl::math::sin( fAnglePi );
377     }
378     else if( fAnglePositiveDegree<= 180.0 )
379     {
380         double beta = fAnglePi-F_PI/2.0;
381         rfXCorrection = aSize.Width*rtl::math::sin( beta )
382                         +aSize.Height*rtl::math::cos( beta );
383         rfYCorrection = -aSize.Height*rtl::math::sin( beta );
384     }
385     else if( fAnglePositiveDegree<= 270.0 )
386     {
387         double beta = 3*F_PI/2.0 - fAnglePi;
388         rfXCorrection = aSize.Width*rtl::math::sin( beta );
389         rfYCorrection = -aSize.Width*rtl::math::cos( beta )
390                         -aSize.Height*rtl::math::sin( beta );
391     }
392     else
393     {
394         rfYCorrection = aSize.Width*rtl::math::sin( fAnglePi );
395     }
396 }
397 
398 void lcl_correctRotation_Right_Bottom( double& rfXCorrection, double& rfYCorrection
399                            , double fAnglePositiveDegree, const awt::Size& aSize )
400 {
401     //correct position for labels at the right bottom corner of something with a top left alignment
402     double fAnglePi = fAnglePositiveDegree*F_PI/180.0;
403     if( fAnglePositiveDegree==0.0 )
404     {
405     }
406     else if( fAnglePositiveDegree<= 90.0 )
407     {
408         rfYCorrection = aSize.Width*rtl::math::sin( fAnglePi );
409     }
410     else if( fAnglePositiveDegree<= 180.0 )
411     {
412         double beta = fAnglePi-F_PI/2.0;
413         rfXCorrection = aSize.Width*rtl::math::sin( beta );
414         rfYCorrection = aSize.Height*rtl::math::sin( beta )
415                         +aSize.Width*rtl::math::cos( beta );
416     }
417     else if( fAnglePositiveDegree<= 270.0 )
418     {
419         double beta = 3*F_PI/2.0 - fAnglePi;
420         rfXCorrection = aSize.Height*rtl::math::cos( beta )
421                         +aSize.Width*rtl::math::sin( beta );
422         rfYCorrection = aSize.Height*rtl::math::sin( beta );
423     }
424     else
425     {
426         rfXCorrection = -aSize.Height*rtl::math::sin( fAnglePi );
427     }
428 }
429 
430 }//end anonymous namespace
431 
432 void LabelPositionHelper::correctPositionForRotation( const uno::Reference< drawing::XShape >& xShape2DText
433                      , LabelAlignment eLabelAlignment, const double fRotationAngle, bool bRotateAroundCenter )
434 {
435     if( !xShape2DText.is() )
436         return;
437 
438     awt::Point aOldPos = xShape2DText->getPosition();
439     awt::Size  aSize   = xShape2DText->getSize();
440 
441     double fYCorrection = 0.0;
442     double fXCorrection  = 0.0;
443 
444     double fAnglePositiveDegree = fRotationAngle;
445     while(fAnglePositiveDegree<0.0)
446         fAnglePositiveDegree+=360.0;
447 
448     switch(eLabelAlignment)
449     {
450         case LABEL_ALIGN_LEFT:
451             lcl_correctRotation_Left( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
452             break;
453         case LABEL_ALIGN_RIGHT:
454             lcl_correctRotation_Right( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
455             break;
456         case LABEL_ALIGN_TOP:
457             lcl_correctRotation_Top( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
458             break;
459         case LABEL_ALIGN_BOTTOM:
460             lcl_correctRotation_Bottom( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
461             break;
462         case LABEL_ALIGN_LEFT_TOP:
463             lcl_correctRotation_Left_Top( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
464             break;
465         case LABEL_ALIGN_LEFT_BOTTOM:
466             lcl_correctRotation_Left_Bottom( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
467             break;
468         case LABEL_ALIGN_RIGHT_TOP:
469             lcl_correctRotation_Right_Top( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
470             break;
471         case LABEL_ALIGN_RIGHT_BOTTOM:
472             lcl_correctRotation_Right_Bottom( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
473             break;
474         default: //LABEL_ALIGN_CENTER
475             break;
476     }
477 
478     xShape2DText->setPosition( awt::Point(
479           static_cast<sal_Int32>(aOldPos.X + fXCorrection  )
480         , static_cast<sal_Int32>(aOldPos.Y + fYCorrection ) ) );
481 }
482 
483 //.............................................................................
484 } //namespace chart
485 //.............................................................................
486