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_slideshow.hxx"
30 
31 // must be first
32 #include <canvas/debug.hxx>
33 
34 #include <basegfx/range/b2drange.hxx>
35 #include <basegfx/range/b1drange.hxx>
36 #include <basegfx/range/b2dpolyrange.hxx>
37 #include <basegfx/matrix/b2dhommatrix.hxx>
38 #include <basegfx/polygon/b2dpolypolygon.hxx>
39 #include <basegfx/polygon/b2dpolypolygontools.hxx>
40 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
41 
42 #include "layer.hxx"
43 
44 #include <boost/bind.hpp>
45 
46 
47 using namespace ::com::sun::star;
48 
49 namespace slideshow
50 {
51     namespace internal
52     {
53         Layer::Layer( const basegfx::B2DRange& rMaxLayerBounds,
54                       Dummy                     ) :
55             maViewEntries(),
56             maBounds(),
57             maNewBounds(),
58             maMaxBounds( rMaxLayerBounds ),
59             mbBoundsDirty(false),
60             mbBackgroundLayer(true),
61             mbClipSet(false)
62         {
63         }
64 
65         Layer::Layer( const basegfx::B2DRange& rMaxLayerBounds ) :
66             maViewEntries(),
67             maBounds(),
68             maNewBounds(),
69             maMaxBounds( rMaxLayerBounds ),
70             mbBoundsDirty(false),
71             mbBackgroundLayer(false),
72             mbClipSet(false)
73         {
74         }
75 
76         ViewLayerSharedPtr Layer::addView( const ViewSharedPtr& rNewView )
77         {
78             OSL_ASSERT( rNewView );
79 
80             ViewEntryVector::iterator aIter;
81             const ViewEntryVector::iterator aEnd( maViewEntries.end() );
82             if( (aIter=std::find_if( maViewEntries.begin(),
83                                      aEnd,
84                                      boost::bind<bool>(
85                                          std::equal_to< ViewSharedPtr >(),
86                                          boost::bind( &ViewEntry::getView, _1 ),
87                                          boost::cref( rNewView )))) != aEnd )
88             {
89                 // already added - just return existing layer
90                 return aIter->mpViewLayer;
91 
92             }
93 
94             // not yet added - create new view layer
95             ViewLayerSharedPtr pNewLayer;
96             if( mbBackgroundLayer )
97                 pNewLayer = rNewView;
98             else
99                 pNewLayer = rNewView->createViewLayer(maBounds);
100 
101             // add to local list
102             maViewEntries.push_back(
103                 ViewEntry( rNewView,
104                            pNewLayer ));
105 
106             return maViewEntries.back().mpViewLayer;
107         }
108 
109         ViewLayerSharedPtr Layer::removeView( const ViewSharedPtr& rView )
110         {
111             OSL_ASSERT( rView );
112 
113             ViewEntryVector::iterator       aIter;
114             const ViewEntryVector::iterator aEnd( maViewEntries.end() );
115             if( (aIter=std::find_if( maViewEntries.begin(),
116                                        aEnd,
117                                        boost::bind<bool>(
118                                            std::equal_to< ViewSharedPtr >(),
119                                            boost::bind( &ViewEntry::getView, _1 ),
120                                            boost::cref( rView )))) == aEnd )
121             {
122                 // View was not added/is already removed
123                 return ViewLayerSharedPtr();
124             }
125 
126             OSL_ENSURE( std::count_if( maViewEntries.begin(),
127                                        aEnd,
128                                        boost::bind<bool>(
129                                            std::equal_to< ViewSharedPtr >(),
130                                            boost::bind( &ViewEntry::getView, _1 ),
131                                            boost::cref( rView ))) == 1,
132                         "Layer::removeView(): view added multiple times" );
133 
134             ViewLayerSharedPtr pRet( aIter->mpViewLayer );
135             maViewEntries.erase(aIter);
136 
137             return pRet;
138         }
139 
140         void Layer::viewChanged( const ViewSharedPtr& rChangedView )
141         {
142             ViewEntryVector::iterator aIter;
143             const ViewEntryVector::iterator aEnd( maViewEntries.end() );
144             if( (aIter=std::find_if( maViewEntries.begin(),
145                                      aEnd,
146                                      boost::bind<bool>(
147                                          std::equal_to< ViewSharedPtr >(),
148                                          boost::bind( &ViewEntry::getView, _1 ),
149                                          boost::cref( rChangedView )))) !=
150                 aEnd )
151             {
152                 // adapt size of given ViewLayer - background layer
153                 // resizes with view.
154                 if( !mbBackgroundLayer )
155                     aIter->mpViewLayer->resize(maBounds);
156             }
157         }
158 
159         void Layer::viewsChanged()
160         {
161             // adapt size of given ViewLayer - background layer
162             // resizes with view.
163             if( !mbBackgroundLayer )
164             {
165                 std::for_each( maViewEntries.begin(),
166                                maViewEntries.end(),
167                                boost::bind( &ViewLayer::resize,
168                                             boost::bind( &ViewEntry::getViewLayer,
169                                                          _1 ),
170                                             boost::cref(maBounds)));
171             }
172         }
173 
174         void Layer::setShapeViews( ShapeSharedPtr const& rShape ) const
175         {
176             rShape->clearAllViewLayers();
177 
178             std::for_each( maViewEntries.begin(),
179                            maViewEntries.end(),
180                            boost::bind(&Shape::addViewLayer,
181                                        boost::cref(rShape),
182                                        boost::bind(&ViewEntry::getViewLayer,
183                                                    _1),
184                                        false ));
185         }
186 
187         void Layer::setPriority( const ::basegfx::B1DRange& rPrioRange )
188         {
189             if( !mbBackgroundLayer )
190             {
191                 std::for_each( maViewEntries.begin(),
192                                maViewEntries.end(),
193                                boost::bind( &ViewLayer::setPriority,
194                                             boost::bind( &ViewEntry::getViewLayer,
195                                                          _1 ),
196                                             boost::cref(rPrioRange)));
197             }
198         }
199 
200         void Layer::addUpdateRange( ::basegfx::B2DRange const& rUpdateRange )
201         {
202             // TODO(Q1): move this to B2DMultiRange
203             if( !rUpdateRange.isEmpty() )
204                 maUpdateAreas.appendElement( rUpdateRange,
205                                              basegfx::ORIENTATION_POSITIVE );
206         }
207 
208         void Layer::updateBounds( ShapeSharedPtr const& rShape )
209         {
210             if( !mbBackgroundLayer )
211             {
212                 if( !mbBoundsDirty )
213                     maNewBounds.reset();
214 
215                 maNewBounds.expand( rShape->getUpdateArea() );
216             }
217 
218             mbBoundsDirty = true;
219         }
220 
221         bool Layer::commitBounds()
222         {
223             mbBoundsDirty = false;
224 
225             if( mbBackgroundLayer )
226                 return false;
227 
228             if( maNewBounds == maBounds )
229                 return false;
230 
231             maBounds = maNewBounds;
232             if( std::count_if( maViewEntries.begin(),
233                                maViewEntries.end(),
234                                boost::bind( &ViewLayer::resize,
235                                             boost::bind( &ViewEntry::getViewLayer,
236                                                          _1 ),
237                                             boost::cref(maBounds)) ) == 0 )
238             {
239                 return false;
240             }
241 
242             // layer content invalid, update areas have wrong
243             // coordinates/not sensible anymore.
244             clearUpdateRanges();
245 
246             return true;
247         }
248 
249         void Layer::clearUpdateRanges()
250         {
251             maUpdateAreas.clear();
252         }
253 
254         void Layer::clearContent()
255         {
256             // clear content on all view layers
257             std::for_each( maViewEntries.begin(),
258                            maViewEntries.end(),
259                            boost::bind(
260                                &ViewLayer::clear,
261                                boost::bind(
262                                    &ViewEntry::getViewLayer,
263                                    _1)));
264 
265             // layer content cleared, update areas are not sensible
266             // anymore.
267             clearUpdateRanges();
268         }
269 
270         class LayerEndUpdate : private boost::noncopyable
271         {
272         public:
273             LayerEndUpdate( LayerSharedPtr const& rLayer ) :
274                 mpLayer( rLayer )
275             {}
276 
277             ~LayerEndUpdate() { if(mpLayer) mpLayer->endUpdate(); }
278 
279             void dismiss() { mpLayer.reset(); }
280 
281         private:
282             LayerSharedPtr mpLayer;
283         };
284 
285         Layer::EndUpdater Layer::beginUpdate()
286         {
287             if( maUpdateAreas.count() )
288             {
289                 // perform proper layer update. That means, setup proper
290                 // clipping, and render each shape that intersects with
291                 // the calculated update area
292                 ::basegfx::B2DPolyPolygon aClip( maUpdateAreas.solveCrossovers() );
293                 aClip = ::basegfx::tools::stripNeutralPolygons(aClip);
294                 aClip = ::basegfx::tools::stripDispensablePolygons(aClip, false);
295 
296                 // actually, if there happen to be shapes with zero
297                 // update area in the maUpdateAreas vector, the
298                 // resulting clip polygon will be empty.
299                 if( aClip.count() )
300                 {
301                     // set clip to all view layers
302                     std::for_each( maViewEntries.begin(),
303                                    maViewEntries.end(),
304                                    boost::bind(
305                                        &ViewLayer::setClip,
306                                        boost::bind(
307                                            &ViewEntry::getViewLayer,
308                                            _1),
309                                        boost::cref(aClip)));
310 
311                     // clear update area on all view layers
312                     std::for_each( maViewEntries.begin(),
313                                    maViewEntries.end(),
314                                    boost::bind(
315                                        &ViewLayer::clear,
316                                        boost::bind(
317                                            &ViewEntry::getViewLayer,
318                                            _1)));
319 
320                     mbClipSet = true;
321                 }
322             }
323 
324             return EndUpdater(new LayerEndUpdate(shared_from_this()));
325         }
326 
327         void Layer::endUpdate()
328         {
329             if( mbClipSet )
330             {
331                 mbClipSet = false;
332 
333                 basegfx::B2DPolyPolygon aEmptyClip;
334                 std::for_each( maViewEntries.begin(),
335                                maViewEntries.end(),
336                                boost::bind(
337                                    &ViewLayer::setClip,
338                                    boost::bind(
339                                        &ViewEntry::getViewLayer,
340                                        _1),
341                                    boost::cref(aEmptyClip)));
342             }
343 
344             clearUpdateRanges();
345         }
346 
347         bool Layer::isInsideUpdateArea( ShapeSharedPtr const& rShape ) const
348         {
349             return maUpdateAreas.overlaps( rShape->getUpdateArea() );
350         }
351 
352         LayerSharedPtr Layer::createBackgroundLayer( const basegfx::B2DRange& rMaxLayerBounds )
353         {
354             return LayerSharedPtr(new Layer( rMaxLayerBounds,
355                                              BackgroundLayer ));
356         }
357 
358         LayerSharedPtr Layer::createLayer( const basegfx::B2DRange& rMaxLayerBounds )
359         {
360             return LayerSharedPtr( new Layer( rMaxLayerBounds ) );
361         }
362 
363     }
364 }
365