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