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