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