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