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