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