xref: /AOO41X/main/svx/source/sdr/contact/viewobjectcontact.cxx (revision 5be78d22e6c042fbf34ae49f26275dc40c5231d1)
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
getPrimitive2DSequence() const85         const drawinglayer::primitive2d::Primitive2DSequence& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
isTextAnimationAllowed() const86         bool isTextAnimationAllowed() const { return mbTextAnimationAllowed; }
isGraphicAnimationAllowed() const87         bool isGraphicAnimationAllowed() const { return mbGraphicAnimationAllowed; }
88     };
89 
AnimatedExtractingProcessor2D(const drawinglayer::geometry::ViewInformation2D & rViewInformation,bool bTextAnimationAllowed,bool bGraphicAnimationAllowed)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 
~AnimatedExtractingProcessor2D()101     AnimatedExtractingProcessor2D::~AnimatedExtractingProcessor2D()
102     {
103     }
104 
processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D & rCandidate)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     {
ViewObjectContact(ObjectContact & rObjectContact,ViewContact & rViewContact)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 
~ViewObjectContact()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 
getObjectRange() const213         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 
ActionChanged()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 
triggerLazyInvalidate()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
ActionChildInserted(ViewContact & rChild)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 
checkForPrimitive2DAnimations()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 
createPrimitive2DSequence(const DisplayInfo & rDisplayInfo) const316         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::BColorModifierSharedPtr aBColorModifier(
339                         new basegfx::BColorModifier_interpolate(
340                             aRGBWhite,
341                             0.5));
342                     const drawinglayer::primitive2d::Primitive2DReference xReference(
343                         new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
344                             xRetval,
345                             aBColorModifier));
346 
347                     xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
348                 }
349             }
350 
351             return xRetval;
352         }
353 
getPrimitive2DSequence(const DisplayInfo & rDisplayInfo) const354         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
355         {
356             drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence;
357 
358             // take care of redirectors and create new list
359             ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
360 
361             if(pRedirector)
362             {
363                 xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
364             }
365             else
366             {
367                 xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
368             }
369 
370             // local up-to-date checks. New list different from local one?
371             if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence))
372             {
373                 // has changed, copy content
374                 const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence;
375 
376                 // check for animated stuff
377                 const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
378 
379                 // always update object range when PrimitiveSequence changes
380                 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
381                 const_cast< ViewObjectContact* >(this)->maObjectRange =
382                     drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D);
383             }
384 
385             // return current Primitive2DSequence
386             return mxPrimitive2DSequence;
387         }
388 
isPrimitiveVisible(const DisplayInfo &) const389         bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
390         {
391             // default: always visible
392             return true;
393         }
394 
isPrimitiveGhosted(const DisplayInfo & rDisplayInfo) const395         bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
396         {
397             // default: standard check
398             return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
399         }
400 
getPrimitive2DSequenceHierarchy(DisplayInfo & rDisplayInfo) const401         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const
402         {
403             drawinglayer::primitive2d::Primitive2DSequence xRetval;
404 
405             // check model-view visibility
406             if(isPrimitiveVisible(rDisplayInfo))
407             {
408                 xRetval = getPrimitive2DSequence(rDisplayInfo);
409 
410                 if(xRetval.hasElements())
411                 {
412                     // get ranges
413                     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
414                     const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D));
415                     const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport());
416 
417                     // check geometrical visibility
418                     if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange))
419                     {
420                         // not visible, release
421                         xRetval.realloc(0);
422                     }
423                 }
424             }
425 
426             return xRetval;
427         }
428 
getPrimitive2DSequenceSubHierarchy(DisplayInfo & rDisplayInfo) const429         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const
430         {
431             const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
432             drawinglayer::primitive2d::Primitive2DSequence xSeqRetval;
433 
434             for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
435             {
436                 const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
437 
438                 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
439             }
440 
441             return xSeqRetval;
442         }
443     } // end of namespace contact
444 } // end of namespace sdr
445 
446 //////////////////////////////////////////////////////////////////////////////
447 // eof
448