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