xref: /AOO41X/main/svx/source/sdr/contact/viewobjectcontact.cxx (revision 54628ca40d27d15cc98fe861da7fff7e60c2f7d6)
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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 #include <svx/sdr/contact/viewobjectcontact.hxx>
27 #include <svx/sdr/contact/viewcontact.hxx>
28 #include <svx/sdr/contact/objectcontact.hxx>
29 #include <svx/sdr/contact/displayinfo.hxx>
30 #include <vcl/region.hxx>
31 #include <svx/sdr/animation/objectanimator.hxx>
32 #include <svx/sdr/animation/animationstate.hxx>
33 #include <svx/sdr/contact/viewobjectcontactredirector.hxx>
34 #include <basegfx/numeric/ftools.hxx>
35 #include <basegfx/color/bcolor.hxx>
36 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
37 #include <basegfx/tools/canvastools.hxx>
38 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
39 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
40 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
41 #include <svx/sdr/contact/viewobjectcontactredirector.hxx>
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 using namespace com::sun::star;
46 
47 //////////////////////////////////////////////////////////////////////////////
48 
49 namespace
50 {
51     // animated extractor
52 
53     // Necessary to filter a sequence of animated primitives from
54     // a sequence of primitives to find out if animated or not. The decision for
55     // what to decompose is hard-coded and only done for knowingly animated primitives
56     // to not decompose too deeply and unnecessarily. This implies that the list
57     // which is view-specific needs to be expanded by hand when new animated objects
58     // are added. This may eventually be changed to a dynamically configurable approach
59     // if necessary.
60     class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
61     {
62     protected:
63         // the found animated primitives
64         drawinglayer::primitive2d::Primitive2DSequence  maPrimitive2DSequence;
65 
66         // bitfield
67         // text animation allowed?
68         unsigned                                        mbTextAnimationAllowed : 1;
69 
70         // graphic animation allowed?
71         unsigned                                        mbGraphicAnimationAllowed : 1;
72 
73         // as tooling, the process() implementation takes over API handling and calls this
74         // virtual render method when the primitive implementation is BasePrimitive2D-based.
75         virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate);
76 
77     public:
78         AnimatedExtractingProcessor2D(
79             const drawinglayer::geometry::ViewInformation2D& rViewInformation,
80             bool bTextAnimationAllowed,
81             bool bGraphicAnimationAllowed);
82         virtual ~AnimatedExtractingProcessor2D();
83 
84         // data access
85         const drawinglayer::primitive2d::Primitive2DSequence& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
86         bool isTextAnimationAllowed() const { return mbTextAnimationAllowed; }
87         bool isGraphicAnimationAllowed() const { return mbGraphicAnimationAllowed; }
88     };
89 
90     AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
91         const drawinglayer::geometry::ViewInformation2D& rViewInformation,
92         bool bTextAnimationAllowed,
93         bool bGraphicAnimationAllowed)
94     :   drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
95         maPrimitive2DSequence(),
96         mbTextAnimationAllowed(bTextAnimationAllowed),
97         mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
98     {
99     }
100 
101     AnimatedExtractingProcessor2D::~AnimatedExtractingProcessor2D()
102     {
103     }
104 
105     void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
106     {
107         // known implementation, access directly
108         switch(rCandidate.getPrimitive2DID())
109         {
110             // add and accept animated primitives directly, no need to decompose
111             case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D :
112             case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D :
113             case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D :
114             {
115                 const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate);
116 
117                 if((rSwitchPrimitive.isTextAnimation() && isTextAnimationAllowed())
118                     || (rSwitchPrimitive.isGraphicAnimation() && isGraphicAnimationAllowed()))
119                 {
120                     const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate));
121                     drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(maPrimitive2DSequence, xReference);
122                 }
123                 break;
124             }
125 
126             // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
127             // which then produces the animation infos (all when used/needed)
128             case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D :
129             case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D :
130 
131             // decompose SdrObjects with evtl. animated text
132             case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D :
133             case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D :
134             case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D :
135             case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D :
136             case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D :
137             case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D :
138             case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D :
139             case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D :
140 
141             // #121194# With Graphic as Bitmap FillStyle, also check
142             // for primitives filled with animated graphics
143             case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
144             case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
145             case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
146 
147             // decompose evtl. animated text contained in MaskPrimitive2D
148             // or group rimitives
149             case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
150             case PRIMITIVE2D_ID_GROUPPRIMITIVE2D :
151             {
152                 process(rCandidate.get2DDecomposition(getViewInformation2D()));
153                 break;
154             }
155 
156             default :
157             {
158                 // nothing to do for the rest
159                 break;
160             }
161         }
162     }
163 } // end of anonymous namespace
164 
165 //////////////////////////////////////////////////////////////////////////////
166 
167 namespace sdr
168 {
169     namespace contact
170     {
171         ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact)
172         :   mrObjectContact(rObjectContact),
173             mrViewContact(rViewContact),
174             maObjectRange(),
175             mxPrimitive2DSequence(),
176             mpPrimitiveAnimation(0),
177             mbLazyInvalidate(false)
178         {
179             // make the ViewContact remember me
180             mrViewContact.AddViewObjectContact(*this);
181 
182             // make the ObjectContact remember me
183             mrObjectContact.AddViewObjectContact(*this);
184         }
185 
186         ViewObjectContact::~ViewObjectContact()
187         {
188             // invalidate in view
189             if(!maObjectRange.isEmpty())
190             {
191                 GetObjectContact().InvalidatePartOfView(maObjectRange);
192             }
193 
194             // delete PrimitiveAnimation
195             if(mpPrimitiveAnimation)
196             {
197                 delete mpPrimitiveAnimation;
198                 mpPrimitiveAnimation = 0;
199             }
200 
201             // take care of remebered ObjectContact. Remove from
202             // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
203             // which (depending of it's implementation) may destroy other OCs. This
204             // can trigger the deletion of the helper OC of a page visualising object
205             // which IS the OC of this object. Eventually StopGettingViewed() needs
206             // to get asynchron later
207             GetObjectContact().RemoveViewObjectContact(*this);
208 
209             // take care of remebered ViewContact
210             GetViewContact().RemoveViewObjectContact(*this);
211         }
212 
213         const basegfx::B2DRange& ViewObjectContact::getObjectRange() const
214         {
215             if(maObjectRange.isEmpty())
216             {
217                 // if range is not computed (new or LazyInvalidate objects), force it
218                 const DisplayInfo aDisplayInfo;
219                 const drawinglayer::primitive2d::Primitive2DSequence xSequence(getPrimitive2DSequence(aDisplayInfo));
220 
221                 if(xSequence.hasElements())
222                 {
223                     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
224                     const_cast< ViewObjectContact* >(this)->maObjectRange =
225                         drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xSequence, rViewInformation2D);
226                 }
227             }
228 
229             return maObjectRange;
230         }
231 
232         void ViewObjectContact::ActionChanged()
233         {
234             if(!mbLazyInvalidate)
235             {
236                 // set local flag
237                 mbLazyInvalidate = true;
238 
239                 // force ObjectRange
240                 getObjectRange();
241 
242                 if(!maObjectRange.isEmpty())
243                 {
244                     // invalidate current valid range
245                     GetObjectContact().InvalidatePartOfView(maObjectRange);
246 
247                     // reset ObjectRange, it needs to be recalculated
248                     maObjectRange.reset();
249                 }
250 
251                 // register at OC for lazy invalidate
252                 GetObjectContact().setLazyInvalidate(*this);
253             }
254         }
255 
256         void ViewObjectContact::triggerLazyInvalidate()
257         {
258             if(mbLazyInvalidate)
259             {
260                 // reset flag
261                 mbLazyInvalidate = false;
262 
263                 // force ObjectRange
264                 getObjectRange();
265 
266                 if(!maObjectRange.isEmpty())
267                 {
268                     // invalidate current valid range
269                     GetObjectContact().InvalidatePartOfView(maObjectRange);
270                 }
271             }
272         }
273 
274         // Take some action when new objects are inserted
275         void ViewObjectContact::ActionChildInserted(ViewContact& rChild)
276         {
277             // force creation of the new VOC and trigger it's refresh, so it
278             // will take part in LazyInvalidate immediately
279             rChild.GetViewObjectContact(GetObjectContact()).ActionChanged();
280 
281             // forward action to ObjectContact
282             // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
283             // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
284         }
285 
286         void ViewObjectContact::checkForPrimitive2DAnimations()
287         {
288             // remove old one
289             if(mpPrimitiveAnimation)
290             {
291                 delete mpPrimitiveAnimation;
292                 mpPrimitiveAnimation = 0;
293             }
294 
295             // check for animated primitives
296             if(mxPrimitive2DSequence.hasElements())
297             {
298                 const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
299                 const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
300 
301                 if(bTextAnimationAllowed || bGraphicAnimationAllowed)
302                 {
303                     AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
304                         bTextAnimationAllowed, bGraphicAnimationAllowed);
305                     aAnimatedExtractor.process(mxPrimitive2DSequence);
306 
307                     if(aAnimatedExtractor.getPrimitive2DSequence().hasElements())
308                     {
309                         // dervied primitiveList is animated, setup new PrimitiveAnimation
310                         mpPrimitiveAnimation =  new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence());
311                     }
312                 }
313             }
314         }
315 
316         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
317         {
318             // get the view-independent Primitive from the viewContact
319             drawinglayer::primitive2d::Primitive2DSequence xRetval(GetViewContact().getViewIndependentPrimitive2DSequence());
320 
321             if(xRetval.hasElements())
322             {
323                 // handle GluePoint
324                 if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
325                 {
326                     const drawinglayer::primitive2d::Primitive2DSequence xGlue(GetViewContact().createGluePointPrimitive2DSequence());
327 
328                     if(xGlue.hasElements())
329                     {
330                         drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, xGlue);
331                     }
332                 }
333 
334                 // handle ghosted
335                 if(isPrimitiveGhosted(rDisplayInfo))
336                 {
337                     const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
338                     const basegfx::BColorModifier aBColorModifier(aRGBWhite, 0.5, basegfx::BCOLORMODIFYMODE_INTERPOLATE);
339                     const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::ModifiedColorPrimitive2D(xRetval, aBColorModifier));
340                     xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
341                 }
342             }
343 
344             return xRetval;
345         }
346 
347         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
348         {
349             drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence;
350 
351             // take care of redirectors and create new list
352             ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
353 
354             if(pRedirector)
355             {
356                 xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
357             }
358             else
359             {
360                 xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
361             }
362 
363             // local up-to-date checks. New list different from local one?
364             if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence))
365             {
366                 // has changed, copy content
367                 const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence;
368 
369                 // check for animated stuff
370                 const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
371 
372                 // always update object range when PrimitiveSequence changes
373                 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
374                 const_cast< ViewObjectContact* >(this)->maObjectRange =
375                     drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D);
376             }
377 
378             // return current Primitive2DSequence
379             return mxPrimitive2DSequence;
380         }
381 
382         bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
383         {
384             // default: always visible
385             return true;
386         }
387 
388         bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
389         {
390             // default: standard check
391             return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
392         }
393 
394         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const
395         {
396             drawinglayer::primitive2d::Primitive2DSequence xRetval;
397 
398             // check model-view visibility
399             if(isPrimitiveVisible(rDisplayInfo))
400             {
401                 xRetval = getPrimitive2DSequence(rDisplayInfo);
402 
403                 if(xRetval.hasElements())
404                 {
405                     // get ranges
406                     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
407                     const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D));
408                     const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport());
409 
410                     // check geometrical visibility
411                     if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange))
412                     {
413                         // not visible, release
414                         xRetval.realloc(0);
415                     }
416                 }
417             }
418 
419             return xRetval;
420         }
421 
422         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const
423         {
424             const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
425             drawinglayer::primitive2d::Primitive2DSequence xSeqRetval;
426 
427             for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
428             {
429                 const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
430 
431                 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
432             }
433 
434             return xSeqRetval;
435         }
436     } // end of namespace contact
437 } // end of namespace sdr
438 
439 //////////////////////////////////////////////////////////////////////////////
440 // eof
441