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 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_svx.hxx" 30 #include <svx/sdr/contact/viewcontactofgraphic.hxx> 31 #include <svx/sdr/contact/viewobjectcontactofgraphic.hxx> 32 #include <svx/svdograf.hxx> 33 #include <svx/sdr/primitive2d/sdrattributecreator.hxx> 34 #include <svl/itemset.hxx> 35 36 #ifndef ITEMID_GRF_CROP 37 #define ITEMID_GRF_CROP 0 38 #endif 39 40 #include <svx/sdgcpitm.hxx> 41 #include <svx/sdr/contact/displayinfo.hxx> 42 #include <svx/sdr/contact/viewobjectcontact.hxx> 43 #include <svx/sdr/contact/objectcontact.hxx> 44 #include <svx/sdr/event/eventhandler.hxx> 45 #include <basegfx/matrix/b2dhommatrix.hxx> 46 #include <svx/sdr/primitive2d/sdrgrafprimitive2d.hxx> 47 #include "svx/svdstr.hrc" 48 #include <svx/svdglob.hxx> 49 #include <vcl/svapp.hxx> 50 #include <basegfx/polygon/b2dpolygontools.hxx> 51 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 52 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> 53 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 54 #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 55 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 56 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx> 57 #include <editeng/eeitem.hxx> 58 #include <editeng/colritem.hxx> 59 #include <basegfx/matrix/b2dhommatrixtools.hxx> 60 #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> 61 62 ////////////////////////////////////////////////////////////////////////////// 63 64 namespace sdr 65 { 66 namespace contact 67 { 68 // Create a Object-Specific ViewObjectContact, set ViewContact and 69 // ObjectContact. Always needs to return something. 70 ViewObjectContact& ViewContactOfGraphic::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) 71 { 72 ViewObjectContact* pRetval = new ViewObjectContactOfGraphic(rObjectContact, *this); 73 DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); 74 75 return *pRetval; 76 } 77 78 ViewContactOfGraphic::ViewContactOfGraphic(SdrGrafObj& rGrafObj) 79 : ViewContactOfTextObj(rGrafObj) 80 { 81 } 82 83 ViewContactOfGraphic::~ViewContactOfGraphic() 84 { 85 } 86 87 void ViewContactOfGraphic::flushGraphicObjects() 88 { 89 // #i102380# The graphic is swapped out. To let that have an effect ist is necessary to 90 // delete copies of the GraphicObject which are not swapped out and have no SwapHandler set 91 // (this is what happnes when the GraphicObject gets copied to a SdrGrafPrimitive2D). This 92 // is best achieved for the VC by clearing the local decomposition cache. It would be possible 93 // to also do this for the VOC cache, but that VOCs exist exactly expresss that the object 94 // gets visualised, so this would be wrong. 95 flushViewIndependentPrimitive2DSequence(); 96 } 97 98 drawinglayer::primitive2d::Primitive2DSequence ViewContactOfGraphic::createVIP2DSForPresObj( 99 const basegfx::B2DHomMatrix& rObjectMatrix, 100 const drawinglayer::attribute::SdrLineFillShadowTextAttribute& rAttribute, 101 const GraphicAttr& rLocalGrafInfo) const 102 { 103 drawinglayer::primitive2d::Primitive2DSequence xRetval; 104 GraphicObject aEmptyGraphicObject; 105 GraphicAttr aEmptyGraphicAttr; 106 107 // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts 108 const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( 109 rObjectMatrix, 110 rAttribute, 111 aEmptyGraphicObject, 112 aEmptyGraphicAttr)); 113 xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReferenceA, 1); 114 115 // SdrGrafPrimitive2D with content (which is the preview graphic) scaled to smaller size and 116 // without attributes 117 basegfx::B2DHomMatrix aSmallerMatrix; 118 119 // #i94431# for some reason, i forgot to take the PrefMapMode of the graphic 120 // into account. Since EmptyPresObj's are only used in Draw/Impress, it is 121 // safe to assume 100th mm as target. 122 Size aPrefSize(GetGrafObject().GetGrafPrefSize()); 123 124 if(MAP_PIXEL == GetGrafObject().GetGrafPrefMapMode().GetMapUnit()) 125 { 126 aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MAP_100TH_MM); 127 } 128 else 129 { 130 aPrefSize = Application::GetDefaultDevice()->LogicToLogic(aPrefSize, GetGrafObject().GetGrafPrefMapMode(), MAP_100TH_MM); 131 } 132 133 // decompose object matrix to get single values 134 basegfx::B2DVector aScale, aTranslate; 135 double fRotate, fShearX; 136 rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); 137 138 const double fOffsetX((aScale.getX() - aPrefSize.getWidth()) / 2.0); 139 const double fOffsetY((aScale.getY() - aPrefSize.getHeight()) / 2.0); 140 141 if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0)) 142 { 143 // create the EmptyPresObj fallback visualisation. The fallback graphic 144 // is already provided in rGraphicObject in this case, use it 145 aSmallerMatrix = basegfx::tools::createScaleTranslateB2DHomMatrix(aPrefSize.getWidth(), aPrefSize.getHeight(), fOffsetX, fOffsetY); 146 aSmallerMatrix = basegfx::tools::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) 147 * aSmallerMatrix; 148 149 const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(false); 150 const drawinglayer::primitive2d::Primitive2DReference xReferenceB(new drawinglayer::primitive2d::SdrGrafPrimitive2D( 151 aSmallerMatrix, 152 drawinglayer::attribute::SdrLineFillShadowTextAttribute(), 153 rGraphicObject, 154 rLocalGrafInfo)); 155 156 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, xReferenceB); 157 } 158 159 return xRetval; 160 } 161 162 drawinglayer::primitive2d::Primitive2DSequence ViewContactOfGraphic::createVIP2DSForDraft( 163 const basegfx::B2DHomMatrix& rObjectMatrix, 164 const drawinglayer::attribute::SdrLineFillShadowTextAttribute& rAttribute) const 165 { 166 drawinglayer::primitive2d::Primitive2DSequence xRetval; 167 GraphicObject aEmptyGraphicObject; 168 GraphicAttr aEmptyGraphicAttr; 169 170 // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts 171 const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( 172 rObjectMatrix, 173 rAttribute, 174 aEmptyGraphicObject, 175 aEmptyGraphicAttr)); 176 xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReferenceA, 1); 177 178 if(rAttribute.getLine().isDefault()) 179 { 180 // create a surrounding frame when no linestyle given 181 const Color aColor(Application::GetSettings().GetStyleSettings().GetShadowColor()); 182 const basegfx::BColor aBColor(aColor.getBColor()); 183 basegfx::B2DPolygon aOutline(basegfx::tools::createUnitPolygon()); 184 aOutline.transform(rObjectMatrix); 185 186 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, 187 drawinglayer::primitive2d::Primitive2DReference( 188 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( 189 aOutline, 190 aBColor))); 191 } 192 193 // decompose object matrix to get single values 194 basegfx::B2DVector aScale, aTranslate; 195 double fRotate, fShearX; 196 rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); 197 198 // define a distance value, used for distance from bitmap to borders and from bitmap 199 // to text, too (2 mm) 200 const double fDistance(200.0); 201 202 // consume borders from values 203 aScale.setX(std::max(0.0, aScale.getX() - (2.0 * fDistance))); 204 aScale.setY(std::max(0.0, aScale.getY() - (2.0 * fDistance))); 205 aTranslate.setX(aTranslate.getX() + fDistance); 206 aTranslate.setY(aTranslate.getY() + fDistance); 207 208 // draw a draft bitmap 209 const Bitmap aDraftBitmap(ResId(BMAP_GrafikEi, *ImpGetResMgr())); 210 211 if(!aDraftBitmap.IsEmpty()) 212 { 213 Size aPrefSize(aDraftBitmap.GetPrefSize()); 214 215 if(MAP_PIXEL == aDraftBitmap.GetPrefMapMode().GetMapUnit()) 216 { 217 aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aDraftBitmap.GetSizePixel(), MAP_100TH_MM); 218 } 219 else 220 { 221 aPrefSize = Application::GetDefaultDevice()->LogicToLogic(aPrefSize, aDraftBitmap.GetPrefMapMode(), MAP_100TH_MM); 222 } 223 224 const double fBitmapScaling(2.0); 225 const double fWidth(aPrefSize.getWidth() * fBitmapScaling); 226 const double fHeight(aPrefSize.getHeight() * fBitmapScaling); 227 228 if(basegfx::fTools::more(fWidth, 1.0) 229 && basegfx::fTools::more(fHeight, 1.0) 230 && basegfx::fTools::lessOrEqual(fWidth, aScale.getX()) 231 && basegfx::fTools::lessOrEqual(fHeight, aScale.getY())) 232 { 233 const basegfx::B2DHomMatrix aBitmapMatrix(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( 234 fWidth, fHeight, fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); 235 236 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, 237 drawinglayer::primitive2d::Primitive2DReference( 238 new drawinglayer::primitive2d::BitmapPrimitive2D( 239 BitmapEx(aDraftBitmap), 240 aBitmapMatrix))); 241 242 // consume bitmap size in X 243 aScale.setX(std::max(0.0, aScale.getX() - (fWidth + fDistance))); 244 aTranslate.setX(aTranslate.getX() + fWidth + fDistance); 245 } 246 } 247 248 // Build the text for the draft object 249 XubString aDraftText = GetGrafObject().GetFileName(); 250 251 if(!aDraftText.Len()) 252 { 253 aDraftText = GetGrafObject().GetName(); 254 aDraftText.AppendAscii(" ..."); 255 } 256 257 if(aDraftText.Len() && GetGrafObject().GetModel()) 258 { 259 // #i103255# Goal is to produce TextPrimitives which hold the given text as 260 // BlockText in the available space. It would be very tricky to do 261 // an own word wrap/line layout here. 262 // Using SdrBlockTextPrimitive2D OTOH is critical since it internally 263 // uses the SdrObject it references. To solve this, create a temp 264 // SdrObject with Attributes and Text, generate a SdrBlockTextPrimitive2D 265 // directly and immediately decompose it. After that, it is no longer 266 // needed and can be deleted. 267 268 // create temp RectObj as TextObj and set needed attributes 269 SdrRectObj aRectObj(OBJ_TEXT); 270 aRectObj.SetModel(GetGrafObject().GetModel()); 271 aRectObj.NbcSetText(aDraftText); 272 aRectObj.SetMergedItem(SvxColorItem(Color(COL_LIGHTRED), EE_CHAR_COLOR)); 273 274 // get SdrText and OPO 275 SdrText* pSdrText = aRectObj.getText(0); 276 OutlinerParaObject* pOPO = aRectObj.GetOutlinerParaObject(); 277 278 if(pSdrText && pOPO) 279 { 280 // directly use the remaining space as TextRangeTransform 281 const basegfx::B2DHomMatrix aTextRangeTransform(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( 282 aScale, fShearX, fRotate, aTranslate)); 283 284 // directly create temp SdrBlockTextPrimitive2D 285 drawinglayer::primitive2d::SdrBlockTextPrimitive2D aBlockTextPrimitive( 286 pSdrText, 287 *pOPO, 288 aTextRangeTransform, 289 SDRTEXTHORZADJUST_LEFT, 290 SDRTEXTVERTADJUST_TOP, 291 false, 292 false, 293 false, 294 false, 295 false); 296 297 // decompose immediately with neutral ViewInformation. This will 298 // layout the text to more simple TextPrimitives from drawinglayer 299 const drawinglayer::geometry::ViewInformation2D aViewInformation2D; 300 301 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence( 302 xRetval, 303 aBlockTextPrimitive.get2DDecomposition(aViewInformation2D)); 304 } 305 } 306 307 return xRetval; 308 } 309 310 drawinglayer::primitive2d::Primitive2DSequence ViewContactOfGraphic::createViewIndependentPrimitive2DSequence() const 311 { 312 drawinglayer::primitive2d::Primitive2DSequence xRetval; 313 const SfxItemSet& rItemSet = GetGrafObject().GetMergedItemSet(); 314 drawinglayer::attribute::SdrLineFillShadowTextAttribute aAttribute( 315 drawinglayer::primitive2d::createNewSdrLineFillShadowTextAttribute( 316 rItemSet, 317 GetGrafObject().getText(0))); 318 319 // create and fill GraphicAttr 320 GraphicAttr aLocalGrafInfo; 321 const sal_uInt16 nTrans(((SdrGrafTransparenceItem&)rItemSet.Get(SDRATTR_GRAFTRANSPARENCE)).GetValue()); 322 const SdrGrafCropItem& rCrop((const SdrGrafCropItem&)rItemSet.Get(SDRATTR_GRAFCROP)); 323 aLocalGrafInfo.SetLuminance(((SdrGrafLuminanceItem&)rItemSet.Get(SDRATTR_GRAFLUMINANCE)).GetValue()); 324 aLocalGrafInfo.SetContrast(((SdrGrafContrastItem&)rItemSet.Get(SDRATTR_GRAFCONTRAST)).GetValue()); 325 aLocalGrafInfo.SetChannelR(((SdrGrafRedItem&)rItemSet.Get(SDRATTR_GRAFRED)).GetValue()); 326 aLocalGrafInfo.SetChannelG(((SdrGrafGreenItem&)rItemSet.Get(SDRATTR_GRAFGREEN)).GetValue()); 327 aLocalGrafInfo.SetChannelB(((SdrGrafBlueItem&)rItemSet.Get(SDRATTR_GRAFBLUE)).GetValue()); 328 aLocalGrafInfo.SetGamma(((SdrGrafGamma100Item&)rItemSet.Get(SDRATTR_GRAFGAMMA)).GetValue() * 0.01); 329 aLocalGrafInfo.SetTransparency((sal_uInt8)::basegfx::fround(Min(nTrans, (sal_uInt16)100) * 2.55)); 330 aLocalGrafInfo.SetInvert(((SdrGrafInvertItem&)rItemSet.Get(SDRATTR_GRAFINVERT)).GetValue()); 331 aLocalGrafInfo.SetDrawMode(((SdrGrafModeItem&)rItemSet.Get(SDRATTR_GRAFMODE)).GetValue()); 332 aLocalGrafInfo.SetCrop(rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom()); 333 334 if(aAttribute.isDefault() && 255L != aLocalGrafInfo.GetTransparency()) 335 { 336 // no fill, no line, no text (invisible), but the graphic content is visible. 337 // Create evtl. shadow for content which was not created by createNewSdrLineFillShadowTextAttribute yet 338 const drawinglayer::attribute::SdrShadowAttribute aShadow( 339 drawinglayer::primitive2d::createNewSdrShadowAttribute(rItemSet)); 340 341 if(!aShadow.isDefault()) 342 { 343 // create new attribute set if indeed shadow is used 344 aAttribute = drawinglayer::attribute::SdrLineFillShadowTextAttribute( 345 aAttribute.getLine(), 346 aAttribute.getFill(), 347 aAttribute.getLineStartEnd(), 348 aShadow, 349 aAttribute.getFillFloatTransGradient(), 350 aAttribute.getText()); 351 } 352 } 353 354 // take unrotated snap rect for position and size. Directly use model data, not getBoundRect() or getSnapRect() 355 // which will use the primitive data we just create in the near future 356 const Rectangle& rRectangle = GetGrafObject().GetGeoRect(); 357 const ::basegfx::B2DRange aObjectRange( 358 rRectangle.Left(), rRectangle.Top(), 359 rRectangle.Right(), rRectangle.Bottom()); 360 361 // look for mirroring 362 const GeoStat& rGeoStat(GetGrafObject().GetGeoStat()); 363 const sal_Int32 nDrehWink(rGeoStat.nDrehWink); 364 const bool bRota180(18000 == nDrehWink); 365 const bool bMirrored(GetGrafObject().IsMirrored()); 366 const sal_uInt16 nMirrorCase(bRota180 ? (bMirrored ? 3 : 4) : (bMirrored ? 2 : 1)); 367 bool bHMirr((2 == nMirrorCase ) || (4 == nMirrorCase)); 368 bool bVMirr((3 == nMirrorCase ) || (4 == nMirrorCase)); 369 370 // set mirror flags at LocalGrafInfo. Take into account that the geometry in 371 // aObjectRange is already changed and rotated when bRota180 is used. To rebuild 372 // that old behaviour (as long as part of the model data), correct the H/V flags 373 // accordingly. The created bitmapPrimitive WILL use the rotation, too. 374 if(bRota180) 375 { 376 // if bRota180 which is used for vertical mirroring, the graphic will already be rotated 377 // by 180 degrees. To correct, switch off VMirror and invert HMirroring. 378 bHMirr = !bHMirr; 379 bVMirr = false; 380 } 381 382 if(bHMirr || bVMirr) 383 { 384 aLocalGrafInfo.SetMirrorFlags((bHMirr ? BMP_MIRROR_HORZ : 0)|(bVMirr ? BMP_MIRROR_VERT : 0)); 385 } 386 387 // fill object matrix 388 const double fShearX(rGeoStat.nShearWink ? tan((36000 - rGeoStat.nShearWink) * F_PI18000) : 0.0); 389 const double fRotate(nDrehWink ? (36000 - nDrehWink) * F_PI18000 : 0.0); 390 const basegfx::B2DHomMatrix aObjectMatrix(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( 391 aObjectRange.getWidth(), aObjectRange.getHeight(), 392 fShearX, fRotate, 393 aObjectRange.getMinX(), aObjectRange.getMinY())); 394 395 // get the current, unchenged graphic obect from SdrGrafObj 396 const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(false); 397 398 if(visualisationUsesPresObj()) 399 { 400 // it's an EmptyPresObj, create the SdrGrafPrimitive2D without content and another scaled one 401 // with the content which is the placeholder graphic 402 xRetval = createVIP2DSForPresObj(aObjectMatrix, aAttribute, aLocalGrafInfo); 403 } 404 else if(visualisationUsesDraft()) 405 { 406 // #i102380# The graphic is swapped out. To not force a swap-in here, there is a mechanism 407 // which shows a swapped-out-visualisation (which gets created here now) and an asynchronious 408 // visual update mechanism for swapped-out grapgics when they were loaded (see AsynchGraphicLoadingEvent 409 // and ViewObjectContactOfGraphic implementation). Not forcing the swap-in here allows faster 410 // (non-blocking) processing here and thus in the effect e.g. fast scrolling through pages 411 xRetval = createVIP2DSForDraft(aObjectMatrix, aAttribute); 412 } 413 else 414 { 415 // create primitive. Info: Calling the copy-constructor of GraphicObject in this 416 // SdrGrafPrimitive2D constructor will force a full swap-in of the graphic 417 const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::SdrGrafPrimitive2D( 418 aObjectMatrix, 419 aAttribute, 420 rGraphicObject, 421 aLocalGrafInfo)); 422 423 xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); 424 } 425 426 // always append an invisible outline for the cases where no visible content exists 427 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, 428 drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( 429 false, aObjectMatrix)); 430 431 return xRetval; 432 } 433 434 bool ViewContactOfGraphic::visualisationUsesPresObj() const 435 { 436 return GetGrafObject().IsEmptyPresObj(); 437 } 438 439 bool ViewContactOfGraphic::visualisationUsesDraft() const 440 { 441 // no draft when already PresObj 442 if(visualisationUsesPresObj()) 443 return false; 444 445 // draft when swapped out 446 const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(false); 447 static bool bAllowReplacements(true); 448 449 if(rGraphicObject.IsSwappedOut() && bAllowReplacements) 450 return true; 451 452 // draft when no graphic 453 if(GRAPHIC_NONE == rGraphicObject.GetType() || GRAPHIC_DEFAULT == rGraphicObject.GetType()) 454 return true; 455 456 return false; 457 } 458 459 } // end of namespace contact 460 } // end of namespace sdr 461 462 ////////////////////////////////////////////////////////////////////////////// 463 // eof 464