xref: /trunk/main/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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 #include "precompiled_svx.hxx"
29 #include <svx/sdr/primitive2d/sdrmeasureprimitive2d.hxx>
30 #include <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
31 #include <basegfx/matrix/b2dhommatrix.hxx>
32 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
33 #include <svx/sdr/attribute/sdrtextattribute.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <basegfx/tools/canvastools.hxx>
36 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
37 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
38 #include <basegfx/matrix/b2dhommatrixtools.hxx>
39 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 using namespace com::sun::star;
44 
45 //////////////////////////////////////////////////////////////////////////////
46 
47 namespace drawinglayer
48 {
49     namespace primitive2d
50     {
51         Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
52             const attribute::SdrLineAttribute& rLineAttribute,
53             const basegfx::B2DHomMatrix& rObjectMatrix,
54             const basegfx::B2DPoint& rStart,
55             const basegfx::B2DPoint& rEnd,
56             bool bLeftActive,
57             bool bRightActive) const
58         {
59             const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
60             basegfx::B2DPolygon aPolygon;
61 
62             aPolygon.append(rStart);
63             aPolygon.append(rEnd);
64 
65             if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
66             {
67                 return createPolygonLinePrimitive(
68                     aPolygon,
69                     rObjectMatrix,
70                     rLineAttribute,
71                     attribute::SdrLineStartEndAttribute());
72             }
73 
74             if(bLeftActive && bRightActive)
75             {
76                 return createPolygonLinePrimitive(
77                     aPolygon,
78                     rObjectMatrix,
79                     rLineAttribute,
80                     rLineStartEnd);
81             }
82 
83             const basegfx::B2DPolyPolygon aEmpty;
84             const attribute::SdrLineStartEndAttribute aLineStartEnd(
85                 bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
86                 bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
87                 bLeftActive ? rLineStartEnd.isStartActive() : false, bRightActive ? rLineStartEnd.isEndActive() : false,
88                 bLeftActive ? rLineStartEnd.isStartCentered() : false, bRightActive? rLineStartEnd.isEndCentered() : false);
89 
90             return createPolygonLinePrimitive(aPolygon, rObjectMatrix, rLineAttribute, aLineStartEnd);
91         }
92 
93         Primitive2DSequence SdrMeasurePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& aViewInformation) const
94         {
95             Primitive2DSequence aRetval;
96             SdrBlockTextPrimitive2D* pBlockText = 0;
97             basegfx::B2DRange aTextRange;
98             double fTextX((getStart().getX() + getEnd().getX()) * 0.5);
99             double fTextY((getStart().getX() + getEnd().getX()) * 0.5);
100             const basegfx::B2DVector aLine(getEnd() - getStart());
101             const double fDistance(aLine.getLength());
102             const double fAngle(atan2(aLine.getY(), aLine.getX()));
103             bool bAutoUpsideDown(false);
104             const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
105             const basegfx::B2DHomMatrix aObjectMatrix(
106                 basegfx::tools::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart()));
107 
108             // preapare text, but do not add yet; it needs to be aligned to
109             // the line geometry
110             if(!rTextAttribute.isDefault())
111             {
112                 basegfx::B2DHomMatrix aTextMatrix;
113                 double fTestAngle(fAngle);
114 
115                 if(getTextRotation())
116                 {
117                     aTextMatrix.rotate(-90.0 * F_PI180);
118                     fTestAngle -= (90.0 * F_PI180);
119 
120                     if(getTextAutoAngle() && fTestAngle < -F_PI)
121                     {
122                         fTestAngle += F_2PI;
123                     }
124                 }
125 
126                 if(getTextAutoAngle())
127                 {
128                     if(fTestAngle > (F_PI / 4.0) || fTestAngle < (-F_PI * (3.0 / 4.0)))
129                     {
130                         bAutoUpsideDown = true;
131                     }
132                 }
133 
134                 // create primitive and get text range
135                 pBlockText = new SdrBlockTextPrimitive2D(
136                     &rTextAttribute.getSdrText(),
137                     rTextAttribute.getOutlinerParaObject(),
138                     aTextMatrix,
139                     SDRTEXTHORZADJUST_CENTER,
140                     SDRTEXTVERTADJUST_CENTER,
141                     rTextAttribute.isScroll(),
142                     false,
143                     false,
144                     false,
145                     false);
146 
147                 aTextRange = pBlockText->getB2DRange(aViewInformation);
148             }
149 
150             // prepare line attribute and result
151             {
152                 const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
153                 bool bArrowsOutside(false);
154                 bool bMainLineSplitted(false);
155                 const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
156                 double fStartArrowW(0.0);
157                 double fStartArrowH(0.0);
158                 double fEndArrowW(0.0);
159                 double fEndArrowH(0.0);
160 
161                 if(!rLineStartEnd.isDefault())
162                 {
163                     if(rLineStartEnd.isStartActive())
164                     {
165                         const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getStartPolyPolygon()));
166                         fStartArrowW = rLineStartEnd.getStartWidth();
167                         fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
168 
169                         if(rLineStartEnd.isStartCentered())
170                         {
171                             fStartArrowH *= 0.5;
172                         }
173                     }
174 
175                     if(rLineStartEnd.isEndActive())
176                     {
177                         const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getEndPolyPolygon()));
178                         fEndArrowW = rLineStartEnd.getEndWidth();
179                         fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
180 
181                         if(rLineStartEnd.isEndCentered())
182                         {
183                             fEndArrowH *= 0.5;
184                         }
185                     }
186                 }
187 
188                 const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
189                 const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
190                 const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);
191 
192                 if(fSpaceNeededByArrows > fDistance)
193                 {
194                     bArrowsOutside = true;
195                 }
196 
197                 MeasureTextPosition eHorizontal(getHorizontal());
198                 MeasureTextPosition eVertical(getVertical());
199 
200                 if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
201                 {
202                     eVertical = MEASURETEXTPOSITION_NEGATIVE;
203                 }
204 
205                 if(MEASURETEXTPOSITION_CENTERED == eVertical)
206                 {
207                     bMainLineSplitted = true;
208                 }
209 
210                 if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
211                 {
212                     if(aTextRange.getWidth() > fDistance)
213                     {
214                         eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
215                     }
216                     else
217                     {
218                         eHorizontal = MEASURETEXTPOSITION_CENTERED;
219                     }
220 
221                     if(bMainLineSplitted)
222                     {
223                         if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
224                         {
225                             bArrowsOutside = true;
226                         }
227                     }
228                     else
229                     {
230                         const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));
231 
232                         if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
233                         {
234                             bArrowsOutside = true;
235                         }
236                     }
237                 }
238 
239                 if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
240                 {
241                     bArrowsOutside = true;
242                 }
243 
244                 // switch text above/below?
245                 if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
246                 {
247                     if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
248                     {
249                         eVertical = MEASURETEXTPOSITION_POSITIVE;
250                     }
251                     else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
252                     {
253                         eVertical = MEASURETEXTPOSITION_NEGATIVE;
254                     }
255                 }
256 
257                 const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
258                 const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
259                 const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);
260 
261                 // main line
262                 if(bArrowsOutside)
263                 {
264                     double fLenLeft(fArrowsOutsideLen);
265                     double fLenRight(fArrowsOutsideLen);
266 
267                     if(!bMainLineSplitted)
268                     {
269                         if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
270                         {
271                             fLenLeft = fStartArrowH + aTextRange.getWidth();
272                         }
273                         else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
274                         {
275                             fLenRight = fEndArrowH + aTextRange.getWidth();
276                         }
277                     }
278 
279                     const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
280                     const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());
281 
282                     appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
283                     appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));
284 
285                     if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
286                     {
287                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
288                     }
289                 }
290                 else
291                 {
292                     if(bMainLineSplitted)
293                     {
294                         const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
295                         const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
296                         const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());
297 
298                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
299                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
300                     }
301                     else
302                     {
303                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
304                     }
305                 }
306 
307                 // left/right help line value preparation
308                 const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
309                 const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
310                 const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());
311 
312                 // left help line
313                 const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
314                 const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);
315 
316                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));
317 
318                 // right help line
319                 const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
320                 const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);
321 
322                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));
323 
324                 // text horizontal position
325                 if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
326                 {
327                     // left
328                     const double fSmall(fArrowsOutsideLen * 0.18);
329                     fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);
330 
331                     if(bMainLineSplitted)
332                     {
333                         fTextX -= (fArrowsOutsideLen - fStartArrowH);
334                     }
335 
336                     if(!rTextAttribute.isDefault())
337                     {
338                         fTextX -= rTextAttribute.getTextRightDistance();
339                     }
340                 }
341                 else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
342                 {
343                     // right
344                     const double fSmall(fArrowsOutsideLen * 0.18);
345                     fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);
346 
347                     if(bMainLineSplitted)
348                     {
349                         fTextX += (fArrowsOutsideLen - fEndArrowH);
350                     }
351 
352                     if(!rTextAttribute.isDefault())
353                     {
354                         fTextX += rTextAttribute.getTextLeftDistance();
355                     }
356                 }
357                 else // MEASURETEXTPOSITION_CENTERED
358                 {
359                     // centered
360                     fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);
361 
362                     if(!rTextAttribute.isDefault())
363                     {
364                         fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
365                     }
366                 }
367 
368                 // text vertical position
369                 if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
370                 {
371                     // top
372                     const double fSmall(fArrowsOutsideLen * 0.10);
373                     fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);
374 
375                     if(!rTextAttribute.isDefault())
376                     {
377                         fTextY -= rTextAttribute.getTextLowerDistance();
378                     }
379                 }
380                 else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
381                 {
382                     // bottom
383                     const double fSmall(fArrowsOutsideLen * 0.10);
384                     fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
385 
386                     if(!rTextAttribute.isDefault())
387                     {
388                         fTextY += rTextAttribute.getTextUpperDistance();
389                     }
390                 }
391                 else // MEASURETEXTPOSITION_CENTERED
392                 {
393                     // centered
394                     fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
395 
396                     if(!rTextAttribute.isDefault())
397                     {
398                         fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
399                     }
400                 }
401             }
402 
403             if(getSdrLSTAttribute().getLine().isDefault())
404             {
405                 // embed line geometry to invisible (100% transparent) line group for HitTest
406                 const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(aRetval));
407 
408                 aRetval = Primitive2DSequence(&xHiddenLines, 1);
409             }
410 
411             if(pBlockText)
412             {
413                 // create transformation to text primitive end position
414                 basegfx::B2DHomMatrix aChange;
415 
416                 // handle auto text rotation
417                 if(bAutoUpsideDown)
418                 {
419                     aChange.rotate(F_PI);
420                 }
421 
422                 // move from aTextRange.TopLeft to fTextX, fTextY
423                 aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());
424 
425                 // apply object matrix
426                 aChange *= aObjectMatrix;
427 
428                 // apply to existing text primitive
429                 SdrTextPrimitive2D* pNewBlockText = pBlockText->createTransformedClone(aChange);
430                 OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
431                 delete pBlockText;
432 
433                 // add to local primitives
434                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, Primitive2DReference(pNewBlockText));
435             }
436 
437             // add shadow
438             if(!getSdrLSTAttribute().getShadow().isDefault())
439             {
440                 aRetval = createEmbeddedShadowPrimitive(
441                     aRetval,
442                     getSdrLSTAttribute().getShadow());
443             }
444 
445             return aRetval;
446         }
447 
448         SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
449             const attribute::SdrLineShadowTextAttribute& rSdrLSTAttribute,
450             const basegfx::B2DPoint& rStart,
451             const basegfx::B2DPoint& rEnd,
452             MeasureTextPosition eHorizontal,
453             MeasureTextPosition eVertical,
454             double fDistance,
455             double fUpper,
456             double fLower,
457             double fLeftDelta,
458             double fRightDelta,
459             bool bBelow,
460             bool bTextRotation,
461             bool bTextAutoAngle)
462         :   BufferedDecompositionPrimitive2D(),
463             maSdrLSTAttribute(rSdrLSTAttribute),
464             maStart(rStart),
465             maEnd(rEnd),
466             meHorizontal(eHorizontal),
467             meVertical(eVertical),
468             mfDistance(fDistance),
469             mfUpper(fUpper),
470             mfLower(fLower),
471             mfLeftDelta(fLeftDelta),
472             mfRightDelta(fRightDelta),
473             mbBelow(bBelow),
474             mbTextRotation(bTextRotation),
475             mbTextAutoAngle(bTextAutoAngle)
476         {
477         }
478 
479         bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
480         {
481             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
482             {
483                 const SdrMeasurePrimitive2D& rCompare = (SdrMeasurePrimitive2D&)rPrimitive;
484 
485                 return (getStart() == rCompare.getStart()
486                     && getEnd() == rCompare.getEnd()
487                     && getHorizontal() == rCompare.getHorizontal()
488                     && getVertical() == rCompare.getVertical()
489                     && getDistance() == rCompare.getDistance()
490                     && getUpper() == rCompare.getUpper()
491                     && getLower() == rCompare.getLower()
492                     && getLeftDelta() == rCompare.getLeftDelta()
493                     && getRightDelta() == rCompare.getRightDelta()
494                     && getBelow() == rCompare.getBelow()
495                     && getTextRotation() == rCompare.getTextRotation()
496                     && getTextAutoAngle() == rCompare.getTextAutoAngle()
497                     && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
498             }
499 
500             return false;
501         }
502 
503         // provide unique ID
504         ImplPrimitrive2DIDBlock(SdrMeasurePrimitive2D, PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D)
505 
506     } // end of namespace primitive2d
507 } // end of namespace drawinglayer
508 
509 //////////////////////////////////////////////////////////////////////////////
510 // eof
511