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