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