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