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_slideshow.hxx"
26 
27 #include <canvas/debug.hxx>
28 #include <tools/diagnose_ex.h>
29 #include <com/sun/star/awt/MouseButton.hpp>
30 #include <com/sun/star/awt/SystemPointer.hpp>
31 #include <com/sun/star/presentation/XShapeEventListener.hpp>
32 #include <com/sun/star/presentation/XSlideShowListener.hpp>
33 #include <com/sun/star/awt/MouseButton.hpp>
34 
35 #include "shapemanagerimpl.hxx"
36 
37 #include <boost/bind.hpp>
38 
39 using namespace com::sun::star;
40 
41 namespace slideshow {
42 namespace internal {
43 
ShapeManagerImpl(EventMultiplexer & rMultiplexer,LayerManagerSharedPtr const & rLayerManager,CursorManager & rCursorManager,const ShapeEventListenerMap & rGlobalListenersMap,const ShapeCursorMap & rGlobalCursorMap)44 ShapeManagerImpl::ShapeManagerImpl( EventMultiplexer&            rMultiplexer,
45                                     LayerManagerSharedPtr const& rLayerManager,
46                                     CursorManager&               rCursorManager,
47                                     const ShapeEventListenerMap& rGlobalListenersMap,
48                                     const ShapeCursorMap&        rGlobalCursorMap ):
49     mrMultiplexer(rMultiplexer),
50     mpLayerManager(rLayerManager),
51     mrCursorManager(rCursorManager),
52     mrGlobalListenersMap(rGlobalListenersMap),
53     mrGlobalCursorMap(rGlobalCursorMap),
54     maShapeListenerMap(),
55     maShapeCursorMap(),
56     maHyperlinkShapes(),
57     mbEnabled(false)
58 {
59 }
60 
activate(bool bSlideBackgoundPainted)61 void ShapeManagerImpl::activate( bool bSlideBackgoundPainted )
62 {
63     if( !mbEnabled )
64     {
65         mbEnabled = true;
66 
67         // register this handler on EventMultiplexer.
68         // Higher prio (overrides other engine handlers)
69         mrMultiplexer.addMouseMoveHandler( shared_from_this(), 2.0 );
70         mrMultiplexer.addClickHandler( shared_from_this(), 2.0 );
71         mrMultiplexer.addShapeListenerHandler( shared_from_this() );
72 
73         // clone listener map
74         uno::Reference<presentation::XShapeEventListener> xDummyListener;
75         std::for_each( mrGlobalListenersMap.begin(),
76                        mrGlobalListenersMap.end(),
77                        boost::bind( &ShapeManagerImpl::listenerAdded,
78                                     this,
79                                     boost::cref(xDummyListener),
80                                     boost::bind(
81                                         std::select1st<ShapeEventListenerMap::value_type>(),
82                                         _1 )));
83 
84         // clone cursor map
85         std::for_each( mrGlobalCursorMap.begin(),
86                        mrGlobalCursorMap.end(),
87                        boost::bind( &ShapeManagerImpl::cursorChanged,
88                                     this,
89                                     boost::bind(
90                                         std::select1st<ShapeCursorMap::value_type>(),
91                                         _1 ),
92                                     boost::bind(
93                                         std::select2nd<ShapeCursorMap::value_type>(),
94                                         _1 )));
95 
96         if( mpLayerManager )
97             mpLayerManager->activate( bSlideBackgoundPainted );
98     }
99 }
100 
deactivate()101 void ShapeManagerImpl::deactivate()
102 {
103     if( mbEnabled )
104     {
105         mbEnabled = false;
106 
107         if( mpLayerManager )
108             mpLayerManager->deactivate();
109 
110         maShapeListenerMap.clear();
111         maShapeCursorMap.clear();
112 
113         mrMultiplexer.removeShapeListenerHandler( shared_from_this() );
114         mrMultiplexer.removeMouseMoveHandler( shared_from_this() );
115         mrMultiplexer.removeClickHandler( shared_from_this() );
116     }
117 }
118 
dispose()119 void ShapeManagerImpl::dispose()
120 {
121     // remove listeners (EventMultiplexer holds shared_ptr on us)
122     deactivate();
123 
124     maHyperlinkShapes.clear();
125     maShapeCursorMap.clear();
126     maShapeListenerMap.clear();
127     mpLayerManager.reset();
128 }
129 
handleMousePressed(awt::MouseEvent const &)130 bool ShapeManagerImpl::handleMousePressed( awt::MouseEvent const& )
131 {
132     // not used here
133     return false; // did not handle the event
134 }
135 
handleMouseReleased(awt::MouseEvent const & e)136 bool ShapeManagerImpl::handleMouseReleased( awt::MouseEvent const& e )
137 {
138     if( !mbEnabled || e.Buttons != awt::MouseButton::LEFT)
139         return false;
140 
141     basegfx::B2DPoint const aPosition( e.X, e.Y );
142 
143     // first check for hyperlinks, because these have
144     // highest prio:
145     rtl::OUString const hyperlink( checkForHyperlink(aPosition) );
146     if( hyperlink.getLength() > 0 )
147     {
148         mrMultiplexer.notifyHyperlinkClicked(hyperlink);
149         return true; // event consumed
150     }
151 
152     // find matching shape (scan reversely, to coarsely match
153     // paint order)
154     ShapeToListenersMap::reverse_iterator aCurrBroadcaster(
155         maShapeListenerMap.rbegin() );
156     ShapeToListenersMap::reverse_iterator const aEndBroadcasters(
157         maShapeListenerMap.rend() );
158     while( aCurrBroadcaster != aEndBroadcasters )
159     {
160         // TODO(F2): Get proper geometry polygon from the
161         // shape, to avoid having areas outside the shape
162         // react on the mouse
163         if( aCurrBroadcaster->first->getBounds().isInside( aPosition ) &&
164             aCurrBroadcaster->first->isVisible() )
165         {
166             // shape hit, and shape is visible. Raise
167             // event.
168 
169             boost::shared_ptr<cppu::OInterfaceContainerHelper> const pCont(
170                 aCurrBroadcaster->second );
171             uno::Reference<drawing::XShape> const xShape(
172                 aCurrBroadcaster->first->getXShape() );
173 
174             // DON'T do anything with /this/ after this point!
175             pCont->forEach<presentation::XShapeEventListener>(
176                 boost::bind( &presentation::XShapeEventListener::click,
177                              _1,
178                              boost::cref(xShape),
179                              boost::cref(e) ));
180 
181             return true; // handled this event
182         }
183 
184         ++aCurrBroadcaster;
185     }
186 
187     return false; // did not handle this event
188 }
189 
handleMouseEntered(const awt::MouseEvent &)190 bool ShapeManagerImpl::handleMouseEntered( const awt::MouseEvent& )
191 {
192     // not used here
193     return false; // did not handle the event
194 }
195 
handleMouseExited(const awt::MouseEvent &)196 bool ShapeManagerImpl::handleMouseExited( const awt::MouseEvent& )
197 {
198     // not used here
199     return false; // did not handle the event
200 }
201 
handleMouseDragged(const awt::MouseEvent &)202 bool ShapeManagerImpl::handleMouseDragged( const awt::MouseEvent& )
203 {
204     // not used here
205     return false; // did not handle the event
206 }
207 
handleMouseMoved(const awt::MouseEvent & e)208 bool ShapeManagerImpl::handleMouseMoved( const awt::MouseEvent& e )
209 {
210     if( !mbEnabled )
211         return false;
212 
213     // find hit shape in map
214     const ::basegfx::B2DPoint aPosition( e.X, e.Y );
215     sal_Int16                 nNewCursor(-1);
216 
217     if( checkForHyperlink(aPosition).getLength() > 0 )
218     {
219         nNewCursor = awt::SystemPointer::REFHAND;
220     }
221     else
222     {
223         // find matching shape (scan reversely, to coarsely match
224         // paint order)
225         ShapeToCursorMap::reverse_iterator aCurrCursor(
226             maShapeCursorMap.rbegin() );
227         ShapeToCursorMap::reverse_iterator const aEndCursors(
228             maShapeCursorMap.rend() );
229         while( aCurrCursor != aEndCursors )
230         {
231             // TODO(F2): Get proper geometry polygon from the
232             // shape, to avoid having areas outside the shape
233             // react on the mouse
234             if( aCurrCursor->first->getBounds().isInside( aPosition ) &&
235                 aCurrCursor->first->isVisible() )
236             {
237                 // shape found, and it's visible. set
238                 // requested cursor to shape's
239                 nNewCursor = aCurrCursor->second;
240                 break;
241             }
242 
243             ++aCurrCursor;
244         }
245     }
246 
247     if( nNewCursor == -1 )
248         mrCursorManager.resetCursor();
249     else
250         mrCursorManager.requestCursor( nNewCursor );
251 
252     return false; // we don't /eat/ this event. Lower prio
253                   // handler should see it, too.
254 }
255 
update()256 bool ShapeManagerImpl::update()
257 {
258     if( mbEnabled && mpLayerManager )
259         return mpLayerManager->update();
260 
261     return false;
262 }
263 
update(ViewSharedPtr const &)264 bool ShapeManagerImpl::update( ViewSharedPtr const& /*rView*/ )
265 {
266     // am not doing view-specific updates here.
267     return false;
268 }
269 
needsUpdate() const270 bool ShapeManagerImpl::needsUpdate() const
271 {
272     if( mbEnabled && mpLayerManager )
273         return mpLayerManager->isUpdatePending();
274 
275     return false;
276 }
277 
enterAnimationMode(const AnimatableShapeSharedPtr & rShape)278 void ShapeManagerImpl::enterAnimationMode( const AnimatableShapeSharedPtr& rShape )
279 {
280     if( mbEnabled && mpLayerManager )
281         mpLayerManager->enterAnimationMode(rShape);
282 }
283 
leaveAnimationMode(const AnimatableShapeSharedPtr & rShape)284 void ShapeManagerImpl::leaveAnimationMode( const AnimatableShapeSharedPtr& rShape )
285 {
286     if( mbEnabled && mpLayerManager )
287         mpLayerManager->leaveAnimationMode(rShape);
288 }
289 
notifyShapeUpdate(const ShapeSharedPtr & rShape)290 void ShapeManagerImpl::notifyShapeUpdate( const ShapeSharedPtr& rShape )
291 {
292     if( mbEnabled && mpLayerManager )
293         mpLayerManager->notifyShapeUpdate(rShape);
294 }
295 
lookupShape(uno::Reference<drawing::XShape> const & xShape) const296 ShapeSharedPtr ShapeManagerImpl::lookupShape( uno::Reference< drawing::XShape > const & xShape ) const
297 {
298     if( mpLayerManager )
299         return mpLayerManager->lookupShape(xShape);
300 
301     return ShapeSharedPtr();
302 }
303 
addHyperlinkArea(const HyperlinkAreaSharedPtr & rArea)304 void ShapeManagerImpl::addHyperlinkArea( const HyperlinkAreaSharedPtr& rArea )
305 {
306     maHyperlinkShapes.insert(rArea);
307 }
308 
removeHyperlinkArea(const HyperlinkAreaSharedPtr & rArea)309 void ShapeManagerImpl::removeHyperlinkArea( const HyperlinkAreaSharedPtr& rArea )
310 {
311     maHyperlinkShapes.erase(rArea);
312 }
313 
getSubsetShape(const AttributableShapeSharedPtr & rOrigShape,const DocTreeNode & rTreeNode)314 AttributableShapeSharedPtr ShapeManagerImpl::getSubsetShape( const AttributableShapeSharedPtr& rOrigShape,
315                                                              const DocTreeNode&				   rTreeNode )
316 {
317     if( mpLayerManager )
318         return mpLayerManager->getSubsetShape(rOrigShape,rTreeNode);
319 
320     return AttributableShapeSharedPtr();
321 }
322 
revokeSubset(const AttributableShapeSharedPtr & rOrigShape,const AttributableShapeSharedPtr & rSubsetShape)323 void ShapeManagerImpl::revokeSubset( const AttributableShapeSharedPtr& rOrigShape,
324                                      const AttributableShapeSharedPtr& rSubsetShape )
325 {
326     if( mpLayerManager )
327         mpLayerManager->revokeSubset(rOrigShape,rSubsetShape);
328 }
329 
listenerAdded(const uno::Reference<presentation::XShapeEventListener> &,const uno::Reference<drawing::XShape> & xShape)330 bool ShapeManagerImpl::listenerAdded(
331     const uno::Reference<presentation::XShapeEventListener>& /*xListener*/,
332     const uno::Reference<drawing::XShape>&                   xShape )
333 {
334     ShapeEventListenerMap::const_iterator aIter;
335     if( (aIter = mrGlobalListenersMap.find( xShape )) ==
336         mrGlobalListenersMap.end() )
337     {
338         ENSURE_OR_RETURN_FALSE(false,
339                           "ShapeManagerImpl::listenerAdded(): global "
340                           "shape listener map inconsistency!");
341     }
342 
343     // is this one of our shapes? other shapes are ignored.
344     ShapeSharedPtr pShape( lookupShape(xShape) );
345     if( pShape )
346     {
347         maShapeListenerMap.insert(
348             ShapeToListenersMap::value_type(
349                 pShape,
350                 aIter->second));
351     }
352 
353     return true;
354 }
355 
listenerRemoved(const uno::Reference<presentation::XShapeEventListener> &,const uno::Reference<drawing::XShape> & xShape)356 bool ShapeManagerImpl::listenerRemoved(
357     const uno::Reference<presentation::XShapeEventListener>& /*xListener*/,
358     const uno::Reference<drawing::XShape>&                   xShape )
359 {
360     // shape really erased from map? maybe there are other listeners
361     // for the same shape pending...
362     if( mrGlobalListenersMap.find(xShape) == mrGlobalListenersMap.end() )
363     {
364         // is this one of our shapes? other shapes are ignored.
365         ShapeSharedPtr pShape( lookupShape(xShape) );
366         if( pShape )
367             maShapeListenerMap.erase(pShape);
368     }
369 
370     return true;
371 }
372 
cursorChanged(const uno::Reference<drawing::XShape> & xShape,sal_Int16 nCursor)373 bool ShapeManagerImpl::cursorChanged( const uno::Reference<drawing::XShape>&   xShape,
374                                       sal_Int16                                nCursor )
375 {
376     ShapeSharedPtr pShape( lookupShape(xShape) );
377 
378 	// is this one of our shapes? other shapes are ignored.
379     if( !pShape )
380 		return false;
381 
382     if( mrGlobalCursorMap.find(xShape) == mrGlobalCursorMap.end() )
383     {
384         // erased from global map - erase locally, too
385         maShapeCursorMap.erase(pShape);
386     }
387     else
388     {
389         // included in global map - update local one
390         ShapeToCursorMap::iterator aIter;
391         if( (aIter = maShapeCursorMap.find(pShape))
392             == maShapeCursorMap.end() )
393         {
394             maShapeCursorMap.insert(
395                 ShapeToCursorMap::value_type(
396                     pShape,
397                     nCursor ));
398         }
399         else
400         {
401             aIter->second = nCursor;
402         }
403     }
404 
405     return true;
406 }
407 
checkForHyperlink(basegfx::B2DPoint const & hitPos) const408 rtl::OUString ShapeManagerImpl::checkForHyperlink( basegfx::B2DPoint const& hitPos ) const
409 {
410     // find matching region (scan reversely, to coarsely match
411     // paint order): set is ordered by priority
412     AreaSet::const_reverse_iterator iPos( maHyperlinkShapes.rbegin() );
413     AreaSet::const_reverse_iterator const iEnd( maHyperlinkShapes.rend() );
414     for( ; iPos != iEnd; ++iPos )
415     {
416         HyperlinkAreaSharedPtr const& pArea = *iPos;
417 
418         HyperlinkArea::HyperlinkRegions const linkRegions(
419             pArea->getHyperlinkRegions() );
420 
421         for( std::size_t i = linkRegions.size(); i--; )
422         {
423             basegfx::B2DRange const& region = linkRegions[i].first;
424             if( region.isInside(hitPos) )
425                 return linkRegions[i].second;
426         }
427     }
428 
429     return rtl::OUString();
430 }
431 
addIntrinsicAnimationHandler(const IntrinsicAnimationEventHandlerSharedPtr & rHandler)432 void ShapeManagerImpl::addIntrinsicAnimationHandler( const IntrinsicAnimationEventHandlerSharedPtr& rHandler )
433 {
434     maIntrinsicAnimationEventHandlers.add( rHandler );
435 }
436 
removeIntrinsicAnimationHandler(const IntrinsicAnimationEventHandlerSharedPtr & rHandler)437 void ShapeManagerImpl::removeIntrinsicAnimationHandler( const IntrinsicAnimationEventHandlerSharedPtr& rHandler )
438 {
439     maIntrinsicAnimationEventHandlers.remove( rHandler );
440 }
441 
notifyIntrinsicAnimationsEnabled()442 bool ShapeManagerImpl::notifyIntrinsicAnimationsEnabled()
443 {
444     return maIntrinsicAnimationEventHandlers.applyAll(
445         boost::mem_fn(&IntrinsicAnimationEventHandler::enableAnimations));
446 }
447 
notifyIntrinsicAnimationsDisabled()448 bool ShapeManagerImpl::notifyIntrinsicAnimationsDisabled()
449 {
450     return maIntrinsicAnimationEventHandlers.applyAll(
451         boost::mem_fn(&IntrinsicAnimationEventHandler::disableAnimations));
452 }
453 
454 
455 
456 } // namespace internal
457 } // namespace presentation
458