xref: /AOO41X/main/svx/source/sdr/contact/viewobjectcontact.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
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             // decompose evtl. animated text contained in MaskPrimitive2D
142             // or group rimitives
143             case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
144             case PRIMITIVE2D_ID_GROUPPRIMITIVE2D :
145             {
146                 process(rCandidate.get2DDecomposition(getViewInformation2D()));
147                 break;
148             }
149 
150             default :
151             {
152                 // nothing to do for the rest
153                 break;
154             }
155         }
156     }
157 } // end of anonymous namespace
158 
159 //////////////////////////////////////////////////////////////////////////////
160 
161 namespace sdr
162 {
163     namespace contact
164     {
165         ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact)
166         :   mrObjectContact(rObjectContact),
167             mrViewContact(rViewContact),
168             maObjectRange(),
169             mxPrimitive2DSequence(),
170             mpPrimitiveAnimation(0),
171             mbLazyInvalidate(false)
172         {
173             // make the ViewContact remember me
174             mrViewContact.AddViewObjectContact(*this);
175 
176             // make the ObjectContact remember me
177             mrObjectContact.AddViewObjectContact(*this);
178         }
179 
180         ViewObjectContact::~ViewObjectContact()
181         {
182             // invalidate in view
183             if(!maObjectRange.isEmpty())
184             {
185                 GetObjectContact().InvalidatePartOfView(maObjectRange);
186             }
187 
188             // delete PrimitiveAnimation
189             if(mpPrimitiveAnimation)
190             {
191                 delete mpPrimitiveAnimation;
192                 mpPrimitiveAnimation = 0;
193             }
194 
195             // take care of remebered ObjectContact. Remove from
196             // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
197             // which (depending of it's implementation) may destroy other OCs. This
198             // can trigger the deletion of the helper OC of a page visualising object
199             // which IS the OC of this object. Eventually StopGettingViewed() needs
200             // to get asynchron later
201             GetObjectContact().RemoveViewObjectContact(*this);
202 
203             // take care of remebered ViewContact
204             GetViewContact().RemoveViewObjectContact(*this);
205         }
206 
207         const basegfx::B2DRange& ViewObjectContact::getObjectRange() const
208         {
209             if(maObjectRange.isEmpty())
210             {
211                 // if range is not computed (new or LazyInvalidate objects), force it
212                 const DisplayInfo aDisplayInfo;
213                 const drawinglayer::primitive2d::Primitive2DSequence xSequence(getPrimitive2DSequence(aDisplayInfo));
214 
215                 if(xSequence.hasElements())
216                 {
217                     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
218                     const_cast< ViewObjectContact* >(this)->maObjectRange =
219                         drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xSequence, rViewInformation2D);
220                 }
221             }
222 
223             return maObjectRange;
224         }
225 
226         void ViewObjectContact::ActionChanged()
227         {
228             if(!mbLazyInvalidate)
229             {
230                 // set local flag
231                 mbLazyInvalidate = true;
232 
233                 // force ObjectRange
234                 getObjectRange();
235 
236                 if(!maObjectRange.isEmpty())
237                 {
238                     // invalidate current valid range
239                     GetObjectContact().InvalidatePartOfView(maObjectRange);
240 
241                     // reset ObjectRange, it needs to be recalculated
242                     maObjectRange.reset();
243                 }
244 
245                 // register at OC for lazy invalidate
246                 GetObjectContact().setLazyInvalidate(*this);
247             }
248         }
249 
250         void ViewObjectContact::triggerLazyInvalidate()
251         {
252             if(mbLazyInvalidate)
253             {
254                 // reset flag
255                 mbLazyInvalidate = false;
256 
257                 // force ObjectRange
258                 getObjectRange();
259 
260                 if(!maObjectRange.isEmpty())
261                 {
262                     // invalidate current valid range
263                     GetObjectContact().InvalidatePartOfView(maObjectRange);
264                 }
265             }
266         }
267 
268         // Take some action when new objects are inserted
269         void ViewObjectContact::ActionChildInserted(ViewContact& rChild)
270         {
271             // force creation of the new VOC and trigger it's refresh, so it
272             // will take part in LazyInvalidate immediately
273             rChild.GetViewObjectContact(GetObjectContact()).ActionChanged();
274 
275             // forward action to ObjectContact
276             // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
277             // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
278         }
279 
280         void ViewObjectContact::checkForPrimitive2DAnimations()
281         {
282             // remove old one
283             if(mpPrimitiveAnimation)
284             {
285                 delete mpPrimitiveAnimation;
286                 mpPrimitiveAnimation = 0;
287             }
288 
289             // check for animated primitives
290             if(mxPrimitive2DSequence.hasElements())
291             {
292                 const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
293                 const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
294 
295                 if(bTextAnimationAllowed || bGraphicAnimationAllowed)
296                 {
297                     AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
298                         bTextAnimationAllowed, bGraphicAnimationAllowed);
299                     aAnimatedExtractor.process(mxPrimitive2DSequence);
300 
301                     if(aAnimatedExtractor.getPrimitive2DSequence().hasElements())
302                     {
303                         // dervied primitiveList is animated, setup new PrimitiveAnimation
304                         mpPrimitiveAnimation =  new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence());
305                     }
306                 }
307             }
308         }
309 
310         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
311         {
312             // get the view-independent Primitive from the viewContact
313             drawinglayer::primitive2d::Primitive2DSequence xRetval(GetViewContact().getViewIndependentPrimitive2DSequence());
314 
315             if(xRetval.hasElements())
316             {
317                 // handle GluePoint
318                 if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
319                 {
320                     const drawinglayer::primitive2d::Primitive2DSequence xGlue(GetViewContact().createGluePointPrimitive2DSequence());
321 
322                     if(xGlue.hasElements())
323                     {
324                         drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, xGlue);
325                     }
326                 }
327 
328                 // handle ghosted
329                 if(isPrimitiveGhosted(rDisplayInfo))
330                 {
331                     const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
332                     const basegfx::BColorModifier aBColorModifier(aRGBWhite, 0.5, basegfx::BCOLORMODIFYMODE_INTERPOLATE);
333                     const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::ModifiedColorPrimitive2D(xRetval, aBColorModifier));
334                     xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
335                 }
336             }
337 
338             return xRetval;
339         }
340 
341         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
342         {
343             drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence;
344 
345             // take care of redirectors and create new list
346             ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
347 
348             if(pRedirector)
349             {
350                 xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo);
351             }
352             else
353             {
354                 xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo);
355             }
356 
357             // local up-to-date checks. New list different from local one?
358             if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence))
359             {
360                 // has changed, copy content
361                 const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence;
362 
363                 // check for animated stuff
364                 const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
365 
366                 // always update object range when PrimitiveSequence changes
367                 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
368                 const_cast< ViewObjectContact* >(this)->maObjectRange =
369                     drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D);
370             }
371 
372             // return current Primitive2DSequence
373             return mxPrimitive2DSequence;
374         }
375 
376         bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
377         {
378             // default: always visible
379             return true;
380         }
381 
382         bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
383         {
384             // default: standard check
385             return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
386         }
387 
388         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const
389         {
390             drawinglayer::primitive2d::Primitive2DSequence xRetval;
391 
392             // check model-view visibility
393             if(isPrimitiveVisible(rDisplayInfo))
394             {
395                 xRetval = getPrimitive2DSequence(rDisplayInfo);
396 
397                 if(xRetval.hasElements())
398                 {
399                     // get ranges
400                     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
401                     const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D));
402                     const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport());
403 
404                     // check geometrical visibility
405                     if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange))
406                     {
407                         // not visible, release
408                         xRetval.realloc(0);
409                     }
410                 }
411             }
412 
413             return xRetval;
414         }
415 
416         drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const
417         {
418             const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
419             drawinglayer::primitive2d::Primitive2DSequence xSeqRetval;
420 
421             for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
422             {
423                 const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
424 
425                 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo));
426             }
427 
428             return xSeqRetval;
429         }
430     } // end of namespace contact
431 } // end of namespace sdr
432 
433 //////////////////////////////////////////////////////////////////////////////
434 // eof
435